diff --git a/.circleci/config.yml b/.circleci/config.yml index ed1d54f2d..2f61bf444 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &linux_defaults working_directory: /go/src/github.com/cosmos/cosmos-sdk docker: - - image: circleci/golang:1.11.5 + - image: circleci/golang:1.12.1 environment: GOBIN: /tmp/workspace/bin @@ -17,7 +17,7 @@ macos_config: &macos_defaults xcode: "10.1.0" working_directory: /Users/distiller/project/src/github.com/cosmos/cosmos-sdk environment: - GO_VERSION: "1.11.5" + GO_VERSION: "1.12.1" set_macos_env: &macos_env run: @@ -27,6 +27,7 @@ set_macos_env: &macos_env echo 'export GOPATH=$HOME/project' >> $BASH_ENV echo 'export GOBIN=$GOPATH/bin' >> $BASH_ENV echo 'export PATH=$PATH:$HOME/go/bin:$GOBIN' >> $BASH_ENV + echo 'export GO111MODULE=on' ############ # @@ -35,7 +36,7 @@ set_macos_env: &macos_env docs_update: &docs_deploy working_directory: ~/repo docker: - - image: tendermint/docs_deployment + - image: tendermintdev/jq_curl environment: AWS_REGION: us-east-1 @@ -44,7 +45,6 @@ deps: &dependencies name: dependencies command: | export PATH="$GOBIN:$PATH" - make vendor-deps jobs: setup_dependencies: @@ -53,6 +53,9 @@ jobs: - run: mkdir -p /tmp/workspace/bin - run: mkdir -p /tmp/workspace/profiles - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: tools command: | @@ -63,7 +66,12 @@ jobs: name: binaries command: | export PATH="$GOBIN:$PATH" + make go-mod-cache make install + - save_cache: + key: go-mod-v1-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod" - persist_to_workspace: root: /tmp/workspace paths: @@ -78,17 +86,14 @@ jobs: at: /tmp/workspace - checkout - *dependencies - - run: - name: Get metalinter - command: | - export PATH="$GOBIN:$PATH" - make devtools-clean - make devtools + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Lint source command: | export PATH="$GOBIN:$PATH" - make test_lint + make ci-lint integration_tests: <<: *linux_defaults @@ -98,6 +103,9 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test cli command: | @@ -112,6 +120,9 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test individual module simulations command: | @@ -126,6 +137,9 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test full Gaia simulation command: | @@ -140,6 +154,9 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test Gaia import/export simulation command: | @@ -154,6 +171,9 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test Gaia import/export simulation command: | @@ -168,10 +188,14 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test multi-seed Gaia simulation long command: | export PATH="$GOBIN:$PATH" + export GO111MODULE=on scripts/multisim.sh 500 50 TestFullGaiaSimulation test_sim_gaia_multi_seed: @@ -182,10 +206,14 @@ jobs: at: /tmp/workspace - checkout - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Test multi-seed Gaia simulation short command: | export PATH="$GOBIN:$PATH" + export GO111MODULE=on scripts/multisim.sh 50 10 TestFullGaiaSimulation test_cover: @@ -197,14 +225,18 @@ jobs: - checkout - *dependencies - run: mkdir -p /tmp/logs + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} - run: name: Run tests command: | export PATH="$GOBIN:$PATH" export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')" + export GO111MODULE=on for pkg in $(go list ./... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | grep -v '/simulation' | circleci tests split --split-by=timings); do id=$(echo "$pkg" | sed 's|[/.]|_|g') - GOCACHE=off go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic -tags='ledger test_ledger_mock' "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" + go test -mod=readonly -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic -tags='ledger test_ledger_mock' "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" done - persist_to_workspace: root: /tmp/workspace @@ -253,7 +285,7 @@ jobs: GOPATH: /home/circleci/.go_workspace/ GOOS: linux GOARCH: amd64 - GO_VERSION: "1.11.5" + GO_VERSION: "1.12.1" parallelism: 1 steps: - checkout @@ -268,7 +300,6 @@ jobs: popd set -x make tools - make vendor-deps make build-linux make localnet-start ./scripts/localnet-blocks-test.sh 40 5 10 localhost @@ -280,7 +311,22 @@ jobs: - run: name: Trigger website build command: | - chamber exec cosmos-sdk -- start_website_build + curl --silent \ + --show-error \ + -X POST \ + --header "Content-Type: application/json" \ + -d "{\"branch\": \"$CIRCLE_BRANCH\"}" \ + "https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$WEBSITE_REPO_NAME/build?circle-token=$TENDERBOT_API_TOKEN" > response.json + + RESULT=`jq -r '.status' response.json` + MESSAGE=`jq -r '.message' response.json` + + if [[ ${RESULT} == "null" ]] || [[ ${RESULT} -ne "200" ]]; then + echo "CircleCI API call failed: $MESSAGE" + exit 1 + else + echo "Website build started" + fi macos_ci: <<: *macos_defaults @@ -300,7 +346,6 @@ jobs: command: | source $BASH_ENV make tools - make vendor-deps make install - run: name: Integration tests @@ -322,14 +367,19 @@ jobs: - setup_remote_docker: docker_layer_caching: true - run: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then + GAIAD_VERSION='' + if [ "${CIRCLE_BRANCH}" = "master" ]; then GAIAD_VERSION="stable" - elif [ "${CIRCLE_BRANCH}" == "develop" ]; then + elif [ "${CIRCLE_BRANCH}" = "develop" ]; then GAIAD_VERSION="develop" fi - docker build -t tendermint/gaia:$GAIAD_VERSION . - docker login -u $DOCKER_USER -p $DOCKER_PASS - docker push tendermint/gaia:$GAIAD_VERSION + if [ -z "${GAIAD_VERSION}" ]; then + docker build . + else + docker build -t tendermint/gaia:$GAIAD_VERSION . + docker login -u $DOCKER_USER -p $DOCKER_PASS + docker push tendermint/gaia:$GAIAD_VERSION + fi docker_tagged: <<: *linux_defaults @@ -349,11 +399,6 @@ workflows: test-suite: jobs: - docker_image: - filters: - branches: - only: - - master - - develop requires: - setup_dependencies - docker_tagged: @@ -378,7 +423,12 @@ workflows: only: - master - develop - - setup_dependencies + - setup_dependencies: + # filters here are needed to enable this job also for tags + filters: + tags: + only: + - /^v.*/ - lint: requires: - setup_dependencies diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3a3d666e0..1e722a2c4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,7 +9,7 @@ v If a checkbox is n/a - please still include it but + a little note why - [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work. - [ ] Wrote tests - [ ] Updated relevant documentation (`docs/`) -- [ ] Added entries in `PENDING.md` with issue # +- [ ] Added a relevant changelog entry: `sdkch add [section] [stanza] [message]` - [ ] rereviewed `Files changed` in the github PR explorer ______ diff --git a/.gitignore b/.gitignore index 9f4806524..b6c9df729 100644 --- a/.gitignore +++ b/.gitignore @@ -7,18 +7,17 @@ *.swn .vscode .idea +*.pyc # Build vendor -vendor-deps -.vendor-new build tools/bin/* examples/build/* docs/_build docs/tutorial dist -devtools-stamp +tools-stamp # Data - ideally these don't exist baseapp/data/* diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..ce8010e7a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,17 @@ +linters: + disable-all: true + enable: + - errcheck + - golint + - ineffassign + - unconvert + - misspell +linters-settings: + gocyclo: + min-complexity: 11 + errcheck: + ignore: fmt:.*,io/ioutil:^Read.*,github.com/spf13/cobra:MarkFlagRequired,github.com/spf13/viper:BindPFlag + golint: + min-confidence: 1.1 +run: + tests: false diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c61e9a9..ff1081e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,153 @@ # Changelog +* [0.34.0](#0340) + * [Breaking Changes](#breaking-changes) + * [Gaia](#gaia) + * [Gaia CLI](#gaia-cli) + * [SDK](#sdk) + * [Tendermint](#tendermint) + * [New features](#new-features) + * [SDK](#sdk-1) + * [Gaia](#gaia-1) + * [Gaia CLI](#gaia-cli-1) + * [Gaia REST API](#gaia-rest-api) + * [Improvements](#improvements) + * [Gaia](#gaia-2) + * [Gaia CLI](#gaia-cli-2) + * [SDK](#sdk-2) + * [Bug Fixes](#bug-fixes) + * [Gaia](#gaia-3) + * [Gaia CLI](#gaia-cli-3) + * [SDK](#sdk-3) +* [0.33.2](#0332) + * [Improvements](#improvements-1) + * [Tendermint](#tendermint-1) +* [0.33.1](#0331) + * [Bug Fixes](#bug-fixes-1) + * [Gaia](#gaia-4) + +## 0.34.0 + +### Breaking Changes + +#### Gaia + +* [\#3463](https://github.com/cosmos/cosmos-sdk/issues/3463) Revert bank module handler fork (re-enables transfers) +* [\#3875](https://github.com/cosmos/cosmos-sdk/issues/3875) Replace `async` flag with `--broadcast-mode` flag where the default + value is `sync`. The `block` mode should not be used. The REST client now + uses `mode` parameter instead of the `return` parameter. + +#### Gaia CLI + +* [\#3938](https://github.com/cosmos/cosmos-sdk/issues/3938) Remove REST server's SSL support altogether. + +#### SDK + +* [\#3245](https://github.com/cosmos/cosmos-sdk/issues/3245) Rename validator.GetJailed() to validator.IsJailed() +* [\#3516](https://github.com/cosmos/cosmos-sdk/issues/3516) Remove concept of shares from staking unbonding and redelegation UX; + replaced by direct coin amount. + +#### Tendermint + +* [\#4029](https://github.com/cosmos/cosmos-sdk/issues/4029) Upgrade Tendermint to v0.31.3 + +### New features + +#### SDK + +* [\#2935](https://github.com/cosmos/cosmos-sdk/issues/2935) New module Crisis which can test broken invariant with messages +* [\#3813](https://github.com/cosmos/cosmos-sdk/issues/3813) New sdk.NewCoins safe constructor to replace bare sdk.Coins{} declarations. +* [\#3858](https://github.com/cosmos/cosmos-sdk/issues/3858) add website, details and identity to gentx cli command +* Implement coin conversion and denomination registration utilities + +#### Gaia + +* [\#2935](https://github.com/cosmos/cosmos-sdk/issues/2935) Optionally assert invariants on a blockly basis using `gaiad --assert-invariants-blockly` +* [\#3886](https://github.com/cosmos/cosmos-sdk/issues/3886) Implement minting module querier and CLI/REST clients. + +#### Gaia CLI + +* [\#3937](https://github.com/cosmos/cosmos-sdk/issues/3937) Add command to query community-pool + +#### Gaia REST API + +* [\#3937](https://github.com/cosmos/cosmos-sdk/issues/3937) Add route to fetch community-pool +* [\#3949](https://github.com/cosmos/cosmos-sdk/issues/3949) added /slashing/signing_infos to get signing_info for all validators + +### Improvements + +#### Gaia + +* [\#3808](https://github.com/cosmos/cosmos-sdk/issues/3808) `gaiad` and `gaiacli` integration tests use ./build/ binaries. +* \[\#3819](https://github.com/cosmos/cosmos-sdk/issues/3819) Simulation refactor, log output now stored in ~/.gaiad/simulation/ + * Simulation moved to its own module (not a part of mock) + * Logger type instead of passing function variables everywhere + * Logger json output (for reloadable simulation running) + * Cleanup bank simulation messages / remove dup code in bank simulation + * Simulations saved in `~/.gaiad/simulations/` + * "Lean" simulation output option to exclude No-ops and !ok functions (`--SimulationLean` flag) +* [\#3893](https://github.com/cosmos/cosmos-sdk/issues/3893) Improve `gaiacli tx sign` command + * Add shorthand flags -a and -s for the account and sequence numbers respectively + * Mark the account and sequence numbers required during "offline" mode + * Always do an RPC query for account and sequence number during "online" mode +* [\#4018](https://github.com/cosmos/cosmos-sdk/issues/4018) create genesis port script for release v.0.34.0 + +#### Gaia CLI + +* [\#3833](https://github.com/cosmos/cosmos-sdk/issues/3833) Modify stake to atom in gaia's doc. +* [\#3841](https://github.com/cosmos/cosmos-sdk/issues/3841) Add indent to JSON of `gaiacli keys [add|show|list]` +* [\#3859](https://github.com/cosmos/cosmos-sdk/issues/3859) Add newline to echo of `gaiacli keys ...` +* [\#3959](https://github.com/cosmos/cosmos-sdk/issues/3959) Improving error messages when signing with ledger devices fails + +#### SDK + +* [\#3238](https://github.com/cosmos/cosmos-sdk/issues/3238) Add block time to tx responses when querying for + txs by tags or hash. +* \[\#3752](https://github.com/cosmos/cosmos-sdk/issues/3752) Explanatory docs for minting mechanism (`docs/spec/mint/01_concepts.md`) +* [\#3801](https://github.com/cosmos/cosmos-sdk/issues/3801) `baseapp` safety improvements +* [\#3820](https://github.com/cosmos/cosmos-sdk/issues/3820) Make Coins.IsAllGT() more robust and consistent. +* [\#3828](https://github.com/cosmos/cosmos-sdk/issues/3828) New sdkch tool to maintain changelogs +* [\#3864](https://github.com/cosmos/cosmos-sdk/issues/3864) Make Coins.IsAllGTE() more consistent. +* [\#3907](https://github.com/cosmos/cosmos-sdk/issues/3907): dep -> go mod migration + * Drop dep in favor of go modules. + * Upgrade to Go 1.12.1. +* [\#3917](https://github.com/cosmos/cosmos-sdk/issues/3917) Allow arbitrary decreases to validator commission rates. +* [\#3937](https://github.com/cosmos/cosmos-sdk/issues/3937) Implement community pool querier. +* [\#3940](https://github.com/cosmos/cosmos-sdk/issues/3940) Codespace should be lowercase. +* [\#3986](https://github.com/cosmos/cosmos-sdk/issues/3986) Update the Stringer implementation of the Proposal type. +* [\#926](https://github.com/cosmos/cosmos-sdk/issues/926) circuit breaker high level explanation +* [\#3896](https://github.com/cosmos/cosmos-sdk/issues/3896) Fixed various linters warnings in the context of the gometalinter -> golangci-lint migration +* [\#3916](https://github.com/cosmos/cosmos-sdk/issues/3916) Hex encode data in tx responses + +### Bug Fixes + +#### Gaia + +* [\#3825](https://github.com/cosmos/cosmos-sdk/issues/3825) Validate genesis before running gentx +* [\#3889](https://github.com/cosmos/cosmos-sdk/issues/3889) When `--generate-only` is provided, the Keybase is not used and as a result + the `--from` value must be a valid Bech32 cosmos address. +* 3974 Fix go env setting in installation.md +* 3996 Change 'make get_tools' to 'make tools' in DOCS_README.md. + +#### Gaia CLI + +* [\#3883](https://github.com/cosmos/cosmos-sdk/issues/3883) Remove Height Flag from CLI Queries +* [\#3899](https://github.com/cosmos/cosmos-sdk/issues/3899) Using 'gaiacli config node' breaks ~/config/config.toml + +#### SDK + +* [\#3837](https://github.com/cosmos/cosmos-sdk/issues/3837) Fix `WithdrawValidatorCommission` to properly set the validator's remaining commission. +* [\#3870](https://github.com/cosmos/cosmos-sdk/issues/3870) Fix DecCoins#TruncateDecimal to never return zero coins in + either the truncated coins or the change coins. +* [\#3915](https://github.com/cosmos/cosmos-sdk/issues/3915) Remove ';' delimiting support from ParseDecCoins +* [\#3977](https://github.com/cosmos/cosmos-sdk/issues/3977) Fix docker image build +* [\#4020](https://github.com/cosmos/cosmos-sdk/issues/4020) Fix queryDelegationRewards by returning an error +when the validator or delegation do not exist. +* [\#4050](https://github.com/cosmos/cosmos-sdk/issues/4050) Fix DecCoins APIs +where rounding or truncation could result in zero decimal coins. +* [\#4088](https://github.com/cosmos/cosmos-sdk/issues/4088) Fix `calculateDelegationRewards` +by accounting for rounding errors when multiplying stake by slashing fractions. + ## 0.33.2 ### Improvements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd54c5f02..a6cd69fb4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,21 +100,15 @@ Please don't make Pull Requests to `master`. ## Dependencies -We use [dep](https://github.com/golang/dep) to manage dependencies. +We use [Go 1.11 Modules](https://github.com/golang/go/wiki/Modules) to manage +dependency versions. -That said, the master branch of every Cosmos repository should just build -with `go get`, which means they should be kept up-to-date with their -dependencies so we can get away with telling people they can just `go get` our -software. +The master branch of every Cosmos repository should just build with `go get`, +which means they should be kept up-to-date with their dependencies so we can +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 `dep ensure` (or `make -get_vendor_deps`). Even for dependencies under our control, dep helps us to -keep multiple repos in sync as they evolve. Anything with an executable, such -as apps, tools, and the core, should use dep. - -Run `dep status` to get a list of vendor dependencies that may not be -up-to-date. +build, in which case we can fall back on `go mod tidy -v`. ## Testing @@ -171,7 +165,7 @@ only pull requests targeted directly against master. ### Development Procedure: - the latest state of development is on `develop` - `develop` must never fail `make test` or `make test_cli` - - `develop` should not fail `make test_lint` + - `develop` should not fail `make lint` - no --force onto `develop` (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`) - before submitting a pull request, begin `git rebase` on top of `develop` diff --git a/Dockerfile b/Dockerfile index aea2ecc9d..517c63589 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ FROM golang:alpine AS build-env # Set up dependencies -ENV PACKAGES make git libc-dev bash gcc linux-headers eudev-dev +ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev # Set working directory for the build WORKDIR /go/src/github.com/cosmos/cosmos-sdk @@ -16,8 +16,6 @@ COPY . . # Install minimum necessary dependencies, build Cosmos SDK, remove packages RUN apk add --no-cache $PACKAGES && \ make tools && \ - make vendor-deps && \ - make build && \ make install # Final image diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index bae3081ff..000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,725 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" - name = "github.com/bartekn/go-bip39" - packages = ["."] - pruneopts = "UT" - revision = "a05967ea095d81c8fe4833776774cfaff8e5036c" - -[[projects]] - branch = "master" - digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "UT" - revision = "3a771d992973f24aa725d07868b467d1ddfceafb" - -[[projects]] - digest = "1:1343a2963481a305ca4d051e84bc2abd16b601ee22ed324f8d605de1adb291b0" - name = "github.com/bgentry/speakeasy" - packages = ["."] - pruneopts = "UT" - revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd" - version = "v0.1.0" - -[[projects]] - digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904" - name = "github.com/btcsuite/btcd" - packages = ["btcec"] - pruneopts = "UT" - revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" - -[[projects]] - digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" - name = "github.com/btcsuite/btcutil" - packages = ["bech32"] - pruneopts = "UT" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - -[[projects]] - digest = "1:e8a3550c8786316675ff54ad6f09d265d129c9d986919af7f541afba50d87ce2" - name = "github.com/cosmos/go-bip39" - packages = ["."] - pruneopts = "UT" - revision = "52158e4697b87de16ed390e1bdaf813e581008fa" - -[[projects]] - digest = "1:a2b34d1436ac24a0db176f84c015d80438602eeec12f2f8358bb72749711ab52" - name = "github.com/cosmos/ledger-cosmos-go" - packages = ["."] - pruneopts = "UT" - revision = "e2f595b3b7b222e1cbe9daf89e73e4dcab02d54c" - version = "v0.9.8" - -[[projects]] - digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "UT" - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - digest = "1:fed20bf7f0da387c96d4cfc140a95572e5aba4bb984beb7de910e090ae39849b" - name = "github.com/ethereum/go-ethereum" - packages = ["crypto/secp256k1"] - pruneopts = "UT" - revision = "c942700427557e3ff6de3aaf6b916e2f056c1ec2" - version = "v1.8.23" - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" - name = "github.com/go-kit/kit" - packages = [ - "log", - "log/level", - "log/term", - "metrics", - "metrics/discard", - "metrics/internal/lv", - "metrics/prometheus", - ] - pruneopts = "UT" - revision = "4dc7be5d2d12881735283bcab7352178e190fc71" - version = "v0.6.0" - -[[projects]] - digest = "1:4062bc6de62d73e2be342243cf138cf499b34d558876db8d9430e2149388a4d8" - name = "github.com/go-logfmt/logfmt" - packages = ["."] - pruneopts = "UT" - revision = "07c9b44f60d7ffdfb7d8efe1ad539965737836dc" - version = "v0.4.0" - -[[projects]] - digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" - name = "github.com/go-stack/stack" - packages = ["."] - pruneopts = "UT" - revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" - version = "v1.8.0" - -[[projects]] - digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" - name = "github.com/gogo/protobuf" - packages = [ - "gogoproto", - "jsonpb", - "proto", - "protoc-gen-gogo/descriptor", - "sortkeys", - "types", - ] - pruneopts = "UT" - revision = "636bf0302bc95575d69441b25a2603156ffdddf1" - version = "v1.1.1" - -[[projects]] - digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" - -[[projects]] - digest = "1:e4f5819333ac698d294fe04dbf640f84719658d5c7ce195b10060cc37292ce79" - name = "github.com/golang/snappy" - packages = ["."] - pruneopts = "UT" - revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a" - version = "v0.0.1" - -[[projects]] - digest = "1:ca59b1175189b3f0e9f1793d2c350114be36eaabbe5b9f554b35edee1de50aea" - name = "github.com/gorilla/mux" - packages = ["."] - pruneopts = "UT" - revision = "a7962380ca08b5a188038c69871b8d3fbdf31e89" - version = "v1.7.0" - -[[projects]] - digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d" - name = "github.com/gorilla/websocket" - packages = ["."] - pruneopts = "UT" - revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d" - version = "v1.4.0" - -[[projects]] - digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "UT" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - digest = "1:a74b5a8e34ee5843cd6e65f698f3e75614f812ff170c2243425d75bc091e9af2" - name = "github.com/jmhodges/levigo" - packages = ["."] - pruneopts = "UT" - revision = "853d788c5c416eaaee5b044570784a96c7a26975" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" - name = "github.com/kr/logfmt" - packages = ["."] - pruneopts = "UT" - revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" - -[[projects]] - digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" - name = "github.com/magiconair/properties" - packages = ["."] - pruneopts = "UT" - revision = "c2353362d570a7bfa228149c62842019201cfb71" - version = "v1.8.0" - -[[projects]] - digest = "1:3bb9c8451d199650bfd303e0068d86f135952fead374ad87c09a9b8a2cc4bd7c" - name = "github.com/mattn/go-isatty" - packages = ["."] - pruneopts = "UT" - revision = "369ecd8cea9851e459abb67eb171853e3986591e" - version = "v0.0.6" - -[[projects]] - digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "UT" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - digest = "1:6d4d9067e639d96a804ee9a30fa89c7233c5c5edbfe736072ff00d48c97bccc4" - name = "github.com/otiai10/copy" - packages = ["."] - pruneopts = "UT" - revision = "7e9a647135a142c2669943d4a4d29be015ce9392" - -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "UT" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:93a746f1060a8acbcf69344862b2ceced80f854170e1caae089b2834c5fbf7f4" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "UT" - revision = "505eaef017263e299324067d40ca2c48f6a2cf50" - version = "v0.9.2" - -[[projects]] - branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "UT" - revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" - -[[projects]] - digest = "1:35cf6bdf68db765988baa9c4f10cc5d7dda1126a54bd62e252dbcd0b1fc8da90" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "UT" - revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250" - version = "v0.2.0" - -[[projects]] - branch = "master" - digest = "1:bfc66a1fd78dc8f14d419fd52a5a42db1d572e887ebcc80b98c1a515a8f2d837" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "iostats", - "nfs", - "xfs", - ] - pruneopts = "UT" - revision = "bbced9601137e764853b2fad7ec3e2dc4c504e02" - -[[projects]] - digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64" - name = "github.com/rakyll/statik" - packages = ["fs"] - pruneopts = "UT" - revision = "aa8a7b1baecd0f31a436bf7956fcdcc609a83035" - version = "v0.1.4" - -[[projects]] - digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" - name = "github.com/rcrowley/go-metrics" - packages = ["."] - pruneopts = "UT" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[projects]] - digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758" - name = "github.com/rs/cors" - packages = ["."] - pruneopts = "UT" - revision = "9a47f48565a795472d43519dd49aac781f3034fb" - version = "v1.6.0" - -[[projects]] - digest = "1:3e39bafd6c2f4bf3c76c3bfd16a2e09e016510ad5db90dc02b88e2f565d6d595" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "f4711e4db9e9a1d3887343acb72b2bbfc2f686f5" - version = "v1.2.1" - -[[projects]] - digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8c9545af88b134710ab1cd196795e7f2388358d7" - version = "v1.3.0" - -[[projects]] - digest = "1:645cabccbb4fa8aab25a956cbcbdf6a6845ca736b2c64e197ca7cbb9d210b939" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "UT" - revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" - version = "v0.0.3" - -[[projects]] - digest = "1:1b753ec16506f5864d26a28b43703c58831255059644351bbcb019b843950900" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1" - version = "v1.1.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:ba07ec7953d565ac57a11b123a3bf0c8e848f2c7f637fccb0f9ce4f9e489d69a" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "UT" - revision = "a1b837276271029e31f796ae5d03ba9ffb017244" - version = "v1.0.3" - -[[projects]] - digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require", - ] - pruneopts = "UT" - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - -[[projects]] - digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b" - name = "github.com/syndtr/goleveldb" - packages = [ - "leveldb", - "leveldb/cache", - "leveldb/comparer", - "leveldb/errors", - "leveldb/filter", - "leveldb/iterator", - "leveldb/journal", - "leveldb/memdb", - "leveldb/opt", - "leveldb/storage", - "leveldb/table", - "leveldb/util", - ] - pruneopts = "UT" - revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445" - -[[projects]] - digest = "1:83f5e189eea2baad419a6a410984514266ff690075759c87e9ede596809bd0b8" - name = "github.com/tendermint/btcd" - packages = ["btcec"] - pruneopts = "UT" - revision = "80daadac05d1cd29571fccf27002d79667a88b58" - version = "v0.1.1" - -[[projects]] - digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a" - name = "github.com/tendermint/go-amino" - packages = ["."] - pruneopts = "UT" - revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" - version = "v0.14.1" - -[[projects]] - digest = "1:1bb088f6291e5426e3874a60bca0e481a91a5633395d7e0c427ec3e49b626e7b" - name = "github.com/tendermint/iavl" - packages = ["."] - pruneopts = "UT" - revision = "ac7c35c12e8633a1e9fd0b52a00b900b40f32cd3" - version = "v0.12.1" - -[[projects]] - digest = "1:97ded93050176418272057abf9819a3dea92713a40327e9906ba6deb92896e9d" - name = "github.com/tendermint/tendermint" - packages = [ - "abci/client", - "abci/example/code", - "abci/example/counter", - "abci/example/kvstore", - "abci/server", - "abci/types", - "blockchain", - "cmd/tendermint/commands", - "config", - "consensus", - "consensus/types", - "crypto", - "crypto/armor", - "crypto/ed25519", - "crypto/encoding/amino", - "crypto/merkle", - "crypto/multisig", - "crypto/multisig/bitarray", - "crypto/secp256k1", - "crypto/tmhash", - "crypto/xsalsa20symmetric", - "evidence", - "libs/autofile", - "libs/bech32", - "libs/cli", - "libs/cli/flags", - "libs/clist", - "libs/common", - "libs/db", - "libs/events", - "libs/fail", - "libs/flowrate", - "libs/log", - "libs/pubsub", - "libs/pubsub/query", - "lite", - "lite/client", - "lite/errors", - "lite/proxy", - "mempool", - "node", - "p2p", - "p2p/conn", - "p2p/pex", - "p2p/upnp", - "privval", - "proxy", - "rpc/client", - "rpc/core", - "rpc/core/types", - "rpc/grpc", - "rpc/lib/client", - "rpc/lib/server", - "rpc/lib/types", - "state", - "state/txindex", - "state/txindex/kv", - "state/txindex/null", - "types", - "types/time", - "version", - ] - pruneopts = "UT" - revision = "v0.31.0-dev0-fix0" - -[[projects]] - digest = "1:b73f5e117bc7c6e8fc47128f20db48a873324ad5cfeeebfc505e85c58682b5e4" - name = "github.com/zondax/hid" - packages = ["."] - pruneopts = "T" - revision = "302fd402163c34626286195dfa9adac758334acc" - version = "v0.9.0" - -[[projects]] - digest = "1:f8e4c0b959174a1fa5946b12f1f2ac7ea5651bef20a9e4a8dac55dbffcaa6cd6" - name = "github.com/zondax/ledger-go" - packages = ["."] - pruneopts = "UT" - revision = "69c15f1333a9b6866e5f66096561c7d138894bc5" - version = "v0.8.0" - -[[projects]] - digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d" - name = "golang.org/x/crypto" - packages = [ - "bcrypt", - "blowfish", - "chacha20poly1305", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "hkdf", - "internal/chacha20", - "internal/subtle", - "nacl/box", - "nacl/secretbox", - "openpgp/armor", - "openpgp/errors", - "pbkdf2", - "poly1305", - "ripemd160", - "salsa20/salsa", - ] - pruneopts = "UT" - revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" - source = "https://github.com/tendermint/crypto" - -[[projects]] - digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" - name = "golang.org/x/net" - packages = [ - "context", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "netutil", - "trace", - ] - pruneopts = "UT" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" - -[[projects]] - digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54" - name = "golang.org/x/sys" - packages = [ - "cpu", - "unix", - ] - pruneopts = "UT" - revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "383e8b2c3b9e36c4076b235b32537292176bae20" - -[[projects]] - digest = "1:cbc746de4662c66fd24a037501bd65aa0f8ad0bfca0c92576e0abb88864e3741" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "binarylog/grpc_binarylog_v1", - "codes", - "connectivity", - "credentials", - "credentials/internal", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/binarylog", - "internal/channelz", - "internal/envconfig", - "internal/grpcrand", - "internal/grpcsync", - "internal/syscall", - "internal/transport", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - ] - pruneopts = "UT" - revision = "2fdaae294f38ed9a121193c51ec99fecd3b13eb7" - version = "v1.19.0" - -[[projects]] - digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/bartekn/go-bip39", - "github.com/bgentry/speakeasy", - "github.com/btcsuite/btcd/btcec", - "github.com/cosmos/go-bip39", - "github.com/cosmos/ledger-cosmos-go", - "github.com/gogo/protobuf/proto", - "github.com/golang/protobuf/proto", - "github.com/gorilla/mux", - "github.com/mattn/go-isatty", - "github.com/otiai10/copy", - "github.com/pelletier/go-toml", - "github.com/pkg/errors", - "github.com/rakyll/statik/fs", - "github.com/spf13/cobra", - "github.com/spf13/pflag", - "github.com/spf13/viper", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", - "github.com/tendermint/btcd/btcec", - "github.com/tendermint/go-amino", - "github.com/tendermint/iavl", - "github.com/tendermint/tendermint/abci/server", - "github.com/tendermint/tendermint/abci/types", - "github.com/tendermint/tendermint/blockchain", - "github.com/tendermint/tendermint/cmd/tendermint/commands", - "github.com/tendermint/tendermint/config", - "github.com/tendermint/tendermint/crypto", - "github.com/tendermint/tendermint/crypto/armor", - "github.com/tendermint/tendermint/crypto/ed25519", - "github.com/tendermint/tendermint/crypto/encoding/amino", - "github.com/tendermint/tendermint/crypto/merkle", - "github.com/tendermint/tendermint/crypto/multisig", - "github.com/tendermint/tendermint/crypto/secp256k1", - "github.com/tendermint/tendermint/crypto/tmhash", - "github.com/tendermint/tendermint/crypto/xsalsa20symmetric", - "github.com/tendermint/tendermint/libs/bech32", - "github.com/tendermint/tendermint/libs/cli", - "github.com/tendermint/tendermint/libs/cli/flags", - "github.com/tendermint/tendermint/libs/common", - "github.com/tendermint/tendermint/libs/db", - "github.com/tendermint/tendermint/libs/log", - "github.com/tendermint/tendermint/lite", - "github.com/tendermint/tendermint/lite/errors", - "github.com/tendermint/tendermint/lite/proxy", - "github.com/tendermint/tendermint/node", - "github.com/tendermint/tendermint/p2p", - "github.com/tendermint/tendermint/privval", - "github.com/tendermint/tendermint/proxy", - "github.com/tendermint/tendermint/rpc/client", - "github.com/tendermint/tendermint/rpc/core/types", - "github.com/tendermint/tendermint/rpc/lib/client", - "github.com/tendermint/tendermint/rpc/lib/server", - "github.com/tendermint/tendermint/state", - "github.com/tendermint/tendermint/types", - "github.com/tendermint/tendermint/types/time", - "github.com/tendermint/tendermint/version", - "golang.org/x/crypto/bcrypt", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 565ba42ba..000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,93 +0,0 @@ -[[constraint]] - name = "github.com/bgentry/speakeasy" - version = "~0.1.0" - -[[override]] - name = "github.com/golang/protobuf" - version = "=1.1.0" - -[[constraint]] - name = "github.com/mattn/go-isatty" - version = "~0.0.3" - -[[constraint]] - name = "github.com/spf13/cobra" - version = "~0.0.1" - -[[constraint]] - name = "github.com/spf13/viper" - version = "~1.0.0" - -[[constraint]] - name = "github.com/pkg/errors" - version = "=0.8.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "=1.2.1" - -[[constraint]] - name = "github.com/otiai10/copy" - revision = "7e9a647135a142c2669943d4a4d29be015ce9392" - -[[override]] - name = "github.com/tendermint/go-amino" - version = "v0.14.1" - -[[override]] - name = "github.com/tendermint/iavl" - version = "~v0.12.0" - -[[override]] - name = "github.com/tendermint/tendermint" - revision = "v0.31.0-dev0-fix0" - -[[constraint]] - name = "github.com/cosmos/ledger-cosmos-go" - version = "=v0.9.8" - -## deps without releases: - -[[constraint]] - name = "github.com/btcsuite/btcd" - revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" - -[[override]] - name = "golang.org/x/crypto" - source = "https://github.com/tendermint/crypto" - revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" - -[[constraint]] - name = "github.com/cosmos/go-bip39" - revision = "52158e4697b87de16ed390e1bdaf813e581008fa" - -## transitive deps, with releases: - -[[override]] - name = "github.com/davecgh/go-spew" - version = "=v1.1.0" - -[[constraint]] - name = "github.com/rakyll/statik" - version = "=v0.1.4" - -## transitive deps, without releases: - -[[override]] - name = "github.com/syndtr/goleveldb" - revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445" - -[[override]] - name = "golang.org/x/sys" - revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" - -[[override]] - name = "google.golang.org/genproto" - revision = "383e8b2c3b9e36c4076b235b32537292176bae20" - -[prune] - go-tests = true - unused-packages = true - [[prune.project]] - name = "github.com/zondax/hid" - unused-packages = false diff --git a/Makefile b/Makefile index d939fde1c..456f19cdd 100644 --- a/Makefile +++ b/Makefile @@ -4,11 +4,10 @@ VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') CAT := $(if $(filter $(OS),Windows_NT),type,cat) LEDGER_ENABLED ?= true -GOTOOLS = \ - github.com/golang/dep/cmd/dep \ - github.com/alecthomas/gometalinter \ - github.com/rakyll/statik GOBIN ?= $(GOPATH)/bin +GOSUM := $(shell which gosum) + +export GO111MODULE = on # process build tags @@ -46,9 +45,12 @@ build_tags := $(strip $(build_tags)) ldflags = -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.VendorDirHash=$(shell $(CAT) vendor-deps) \ -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags)" +ifneq ($(GOSUM),) +ldflags += -X github.com/cosmos/cosmos-sdk/version.VendorDirHash=$(shell $(GOSUM) go.sum) +endif + ifeq ($(WITH_CLEVELDB),yes) ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb endif @@ -57,7 +59,7 @@ ldflags := $(strip $(ldflags)) BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' -all: devtools vendor-deps install test_lint test +all: tools install lint test # The below include contains the tools target. include scripts/Makefile @@ -65,36 +67,36 @@ include scripts/Makefile ######################################## ### CI -ci: devtools vendor-deps install test_cover test_lint test +ci: tools install test_cover lint test ######################################## ### Build/Install -build: +build: go.sum ifeq ($(OS),Windows_NT) - go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad - go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli else - go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad - go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli - go build $(BUILD_FLAGS) -o build/gaiareplay ./cmd/gaia/cmd/gaiareplay - go build $(BUILD_FLAGS) -o build/gaiakeyutil ./cmd/gaia/cmd/gaiakeyutil + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiareplay ./cmd/gaia/cmd/gaiareplay + go build -mod=readonly $(BUILD_FLAGS) -o build/gaiakeyutil ./cmd/gaia/cmd/gaiakeyutil endif -build-linux: vendor-deps +build-linux: go.sum LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build update_gaia_lite_docs: @statik -src=client/lcd/swagger-ui -dest=client/lcd -f -install: vendor-deps check-ledger update_gaia_lite_docs - go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad - go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli - go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiareplay - go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiakeyutil +install: go.sum check-ledger update_gaia_lite_docs + go install -mod=readonly $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad + go install -mod=readonly $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli + go install -mod=readonly $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiareplay + go install -mod=readonly $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiakeyutil -install_debug: - go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug +install_debug: go.sum + go install -mod=readonly $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug dist: @bash publish/dist.sh @@ -103,38 +105,13 @@ dist: ######################################## ### Tools & dependencies -check_tools: - @# https://stackoverflow.com/a/25668869 - @echo "Found tools: $(foreach tool,$(notdir $(GOTOOLS)),\ - $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" +go-mod-cache: go.sum + @echo "--> Download go modules to local cache" + @go mod download -update_tools: - @echo "--> Updating tools to correct version" - $(MAKE) --always-make tools - -update_dev_tools: - @echo "--> Downloading linters (this may take awhile)" - $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) - go get -u github.com/tendermint/lint/golint - -devtools: devtools-stamp -devtools-clean: tools-clean -devtools-stamp: tools - @echo "--> Downloading linters (this may take awhile)" - $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) - go get github.com/tendermint/lint/golint - touch $@ - -vendor-deps: tools - @echo "--> Generating vendor directory via dep ensure" - @rm -rf .vendor-new - @dep ensure -v -vendor-only - tar -c vendor/ | sha1sum | cut -d' ' -f1 > $@ - -update_vendor_deps: tools - @echo "--> Running dep ensure" - @rm -rf .vendor-new - @dep ensure -v +go.sum: tools go.mod + @echo "--> Ensure dependencies have not been modified" + @go mod verify draw_deps: tools @# requires brew install graphviz or apt-get install graphviz @@ -142,7 +119,7 @@ draw_deps: tools @goviz -i github.com/cosmos/cosmos-sdk/cmd/gaia/cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png clean: - rm -f devtools-stamp vendor-deps snapcraft-local.yaml + rm -rf snapcraft-local.yaml build/ distclean: clean rm -rf vendor/ @@ -160,34 +137,34 @@ godocs: test: test_unit -test_cli: - @go test -p 4 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -tags=cli_test +test_cli: build + @go test -mod=readonly -p 4 `go list ./cmd/gaia/cli_test/...` -tags=cli_test test_ledger: # First test with mock - @go test `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger test_ledger_mock' + @go test -mod=readonly `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger test_ledger_mock' # Now test with a real device - @go test -v `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger' + @go test -mod=readonly -v `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger' test_unit: - @VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION) -tags='ledger test_ledger_mock' + @VERSION=$(VERSION) go test -mod=readonly $(PACKAGES_NOSIMULATION) -tags='ledger test_ledger_mock' test_race: - @VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION) + @VERSION=$(VERSION) go test -mod=readonly -race $(PACKAGES_NOSIMULATION) test_sim_gaia_nondeterminism: @echo "Running nondeterminism test..." - @go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m + @go test -mod=readonly ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m test_sim_gaia_custom_genesis_fast: @echo "Running custom genesis simulation..." @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationGenesis=${HOME}/.gaiad/config/genesis.json \ + @go test -mod=readonly ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationGenesis=${HOME}/.gaiad/config/genesis.json \ -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h + @go test -mod=readonly ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h test_sim_gaia_import_export: @echo "Running Gaia import/export simulation. This may take several minutes..." @@ -211,31 +188,31 @@ SIM_BLOCK_SIZE ?= 200 SIM_COMMIT ?= true test_sim_gaia_benchmark: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + @go test -mod=readonly -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h test_sim_gaia_profile: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + @go test -mod=readonly -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out test_cover: @export VERSION=$(VERSION); bash -x tests/test_cover.sh -test_lint: - gometalinter --config=tools/gometalinter.json ./... - !(gometalinter --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --exclude 'vendor/*' --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") +lint: tools ci-lint +ci-lint: + golangci-lint run + go vet -composites=false -tests=false ./... find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s - dep status >> /dev/null - !(grep -n branch Gopkg.toml) + go mod verify -format: +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 benchmark: - @go test -bench=. $(PACKAGES_NOSIMULATION) + @go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) ######################################## @@ -287,11 +264,11 @@ snapcraft-local.yaml: snapcraft-local.yaml.in # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: build install install_debug dist clean distclean \ -check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ -test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ +.PHONY: install install_debug dist clean distclean \ +draw_deps test test_cli test_unit \ +test_cover lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ -test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ -devtools-clean +test_sim_gaia_multi_seed test_sim_gaia_import_export \ +go-mod-cache diff --git a/PENDING.md b/PENDING.md deleted file mode 100644 index 591311315..000000000 --- a/PENDING.md +++ /dev/null @@ -1,57 +0,0 @@ -# PENDING CHANGELOG - - - -## BREAKING CHANGES - -### Gaia REST API - -### Gaia CLI - -### Gaia - -### SDK - -### Tendermint - - - -## FEATURES - -### Gaia REST API - -### Gaia CLI - -### Gaia - -### SDK - -### Tendermint - - - -## IMPROVEMENTS - -### Gaia REST API - -### Gaia CLI - -### Gaia - -### SDK - -### Tendermint - - - -## BUG FIXES - -### Gaia REST API - -### Gaia CLI - -### Gaia - -### SDK - -### Tendermint diff --git a/README.md b/README.md index 37b5c4101..f216f2cd6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ It is being used to build `Gaia`, the first implementation of the Cosmos Hub. **WARNING**: The SDK has mostly stabilized, but we are still making some breaking changes. -**Note**: Requires [Go 1.11.5+](https://golang.org/dl/) +**Note**: Requires [Go 1.12.1+](https://golang.org/dl/) ## Cosmos Hub Mainnet diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index b6694a503..b244a98b7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -287,12 +287,25 @@ func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) mainStore.Set(mainConsensusParamsKey, consensusParamsBz) } -// getMaximumBlockGas gets the maximum gas from the consensus params. -func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) { - if app.consensusParams == nil || app.consensusParams.BlockSize == nil { +// 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 { return 0 } - return uint64(app.consensusParams.BlockSize.MaxGas) + + maxGas := app.consensusParams.Block.MaxGas + switch { + case maxGas < -1: + panic(fmt.Sprintf("invalid maximum block gas: %d", maxGas)) + + case maxGas == -1: + return 0 + + default: + return uint64(maxGas) + } } // ---------------------------------------------------------------------------- @@ -510,6 +523,19 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res } } +func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error { + if req.Header.Height < 1 { + 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) + } + + return nil +} + // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { @@ -518,6 +544,10 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg )) } + if err := app.validateHeight(req); err != nil { + panic(err) + } + // Initialize the DeliverTx state. If this is the first block, it should // already be initialized in InitChain. Otherwise app.deliverState will be // nil, since it is reset on Commit. @@ -631,7 +661,7 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Con // runMsgs iterates through all the messages and executes them. func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) { - idxlogs := make([]sdk.ABCIMessageLog, 0, len(msgs)) // a list of JSON-encoded logs with msg index + idxLogs := make([]sdk.ABCIMessageLog, 0, len(msgs)) // a list of JSON-encoded logs with msg index var data []byte // NOTE: we just append them all (?!) var tags sdk.Tags // also just append them all @@ -665,7 +695,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re // stop execution and return on first failed message if !msgResult.IsOK() { idxLog.Success = false - idxlogs = append(idxlogs, idxLog) + idxLogs = append(idxLogs, idxLog) code = msgResult.Code codespace = msgResult.Codespace @@ -673,10 +703,10 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re } idxLog.Success = true - idxlogs = append(idxlogs, idxLog) + idxLogs = append(idxLogs, idxLog) } - logJSON := codec.Cdc.MustMarshalJSON(idxlogs) + logJSON := codec.Cdc.MustMarshalJSON(idxLogs) result = sdk.Result{ Code: code, Codespace: codespace, @@ -689,7 +719,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re return result } -// Returns the applicantion's deliverState if app is in runTxModeDeliver, +// Returns the applications's deliverState if app is in runTxModeDeliver, // otherwise it returns the application's checkstate. func (app *BaseApp) getState(mode runTxMode) *state { if mode == runTxModeCheck || mode == runTxModeSimulate { @@ -799,7 +829,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // performance benefits, but it'll be more difficult to get right. anteCtx, msCache = app.cacheTxContext(ctx, txBytes) - newCtx, result, abort := app.anteHandler(anteCtx, tx, (mode == runTxModeSimulate)) + newCtx, result, abort := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate) if !newCtx.IsZero() { // At this point, newCtx.MultiStore() is cache-wrapped, or something else // replaced by the ante handler. We want the original multistore, not one diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index c1fca6f91..c3d435f9f 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -296,6 +296,7 @@ func TestInitChainer(t *testing.T) { // stores are mounted and private members are set - sealing baseapp err := app.LoadLatestVersion(capKey) // 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 @@ -308,6 +309,7 @@ func TestInitChainer(t *testing.T) { app.Commit() res = app.Query(query) + require.Equal(t, int64(1), app.LastBlockHeight()) require.Equal(t, value, res.Value) // reload app @@ -316,14 +318,17 @@ func TestInitChainer(t *testing.T) { app.MountStores(capKey, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil require.Nil(t, err) + require.Equal(t, int64(1), app.LastBlockHeight()) // ensure we can still query after reloading res = app.Query(query) require.Equal(t, value, res.Value) // commit and ensure we can still query - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.Commit() + res = app.Query(query) require.Equal(t, value, res.Value) } @@ -514,7 +519,6 @@ func TestCheckTx(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) nTxs := int64(5) - app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder @@ -536,7 +540,8 @@ func TestCheckTx(t *testing.T) { require.Equal(t, nTxs, storedCounter) // If a block is committed, CheckTx state should be reset. - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.EndBlock(abci.RequestEndBlock{}) app.Commit() @@ -567,16 +572,22 @@ func TestDeliverTx(t *testing.T) { nBlocks := 3 txPerHeight := 5 + for blockN := 0; blockN < nBlocks; blockN++ { - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.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) require.NoError(t, err) + res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) } + app.EndBlock(abci.RequestEndBlock{}) app.Commit() } @@ -610,48 +621,47 @@ func TestMultiMsgDeliverTx(t *testing.T) { // run a multi-msg tx // with all msgs the same route - { - app.BeginBlock(abci.RequestBeginBlock{}) - tx := newTxCounter(0, 0, 1, 2) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) - require.NoError(t, err) - res := app.DeliverTx(txBytes) - require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) - store := app.deliverState.ctx.KVStore(capKey1) + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + tx := newTxCounter(0, 0, 1, 2) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + res := app.DeliverTx(txBytes) + require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) - // tx counter only incremented once - txCounter := getIntFromStore(store, anteKey) - require.Equal(t, int64(1), txCounter) + store := app.deliverState.ctx.KVStore(capKey1) - // msg counter incremented three times - msgCounter := getIntFromStore(store, deliverKey) - require.Equal(t, int64(3), msgCounter) - } + // tx counter only incremented once + txCounter := getIntFromStore(store, anteKey) + require.Equal(t, int64(1), txCounter) + + // msg counter incremented three times + msgCounter := getIntFromStore(store, deliverKey) + require.Equal(t, int64(3), msgCounter) // replace the second message with a msgCounter2 - { - tx := newTxCounter(1, 3) - tx.Msgs = append(tx.Msgs, msgCounter2{0}) - tx.Msgs = append(tx.Msgs, msgCounter2{1}) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) - require.NoError(t, err) - res := app.DeliverTx(txBytes) - require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) - store := app.deliverState.ctx.KVStore(capKey1) + tx = newTxCounter(1, 3) + tx.Msgs = append(tx.Msgs, msgCounter2{0}) + tx.Msgs = append(tx.Msgs, msgCounter2{1}) + txBytes, err = codec.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + res = app.DeliverTx(txBytes) + require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) - // tx counter only incremented once - txCounter := getIntFromStore(store, anteKey) - require.Equal(t, int64(2), txCounter) + store = app.deliverState.ctx.KVStore(capKey1) - // original counter increments by one - // new counter increments by two - msgCounter := getIntFromStore(store, deliverKey) - require.Equal(t, int64(4), msgCounter) - msgCounter2 := getIntFromStore(store, deliverKey2) - require.Equal(t, int64(2), msgCounter2) - } + // tx counter only incremented once + txCounter = getIntFromStore(store, anteKey) + require.Equal(t, int64(2), txCounter) + + // original counter increments by one + // new counter increments by two + msgCounter = getIntFromStore(store, deliverKey) + require.Equal(t, int64(4), msgCounter) + msgCounter2 := getIntFromStore(store, deliverKey2) + require.Equal(t, int64(2), msgCounter2) } // Interleave calls to Check and Deliver and ensure @@ -692,7 +702,8 @@ func TestSimulateTx(t *testing.T) { nBlocks := 3 for blockN := 0; blockN < nBlocks; blockN++ { count := int64(blockN + 1) - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: count} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(count, count) txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) @@ -737,7 +748,9 @@ func TestRunInvalidTransaction(t *testing.T) { } app := setupBaseApp(t, anteOpt, routerOpt) - app.BeginBlock(abci.RequestBeginBlock{}) + + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) // Transaction with no messages { @@ -847,7 +860,8 @@ func TestTxGasLimits(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) testCases := []struct { tx *txTest @@ -932,7 +946,7 @@ func TestMaxBlockGasLimits(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{ ConsensusParams: &abci.ConsensusParams{ - BlockSize: &abci.BlockSizeParams{ + Block: &abci.BlockParams{ MaxGas: 100, }, }, @@ -962,7 +976,8 @@ func TestMaxBlockGasLimits(t *testing.T) { tx := tc.tx // reset the block gas - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute the transaction multiple times for j := 0; j < tc.numDelivers; j++ { @@ -1005,7 +1020,9 @@ func TestBaseAppAnteHandler(t *testing.T) { app.InitChain(abci.RequestInitChain{}) registerTestCodec(cdc) - app.BeginBlock(abci.RequestBeginBlock{}) + + header := abci.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute a tx that will fail ante handler execution // @@ -1105,14 +1122,16 @@ func TestGasConsumptionBadTx(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{ ConsensusParams: &abci.ConsensusParams{ - BlockSize: &abci.BlockSizeParams{ + Block: &abci.BlockParams{ MaxGas: 9, }, }, }) app.InitChain(abci.RequestInitChain{}) - app.BeginBlock(abci.RequestBeginBlock{}) + + header := abci.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(5, 0) tx.setFailOnAnte(true) @@ -1174,7 +1193,9 @@ func TestQuery(t *testing.T) { require.Equal(t, 0, len(res.Value)) // query is still empty after a DeliverTx before we commit - app.BeginBlock(abci.RequestBeginBlock{}) + header := abci.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + resTx = app.Deliver(tx) require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) res = app.Query(query) @@ -1216,3 +1237,19 @@ func TestP2PQuery(t *testing.T) { res = app.Query(idQuery) require.Equal(t, uint32(4), res.Code) } + +func TestGetMaximumBlockGas(t *testing.T) { + app := setupBaseApp(t) + + app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 0}}) + require.Equal(t, uint64(0), app.getMaximumBlockGas()) + + app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -1}}) + require.Equal(t, uint64(0), app.getMaximumBlockGas()) + + app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 5000000}}) + require.Equal(t, uint64(5000000), app.getMaximumBlockGas()) + + app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -5000000}}) + require.Panics(t, func() { app.getMaximumBlockGas() }) +} diff --git a/client/config.go b/client/config.go index ce57434a5..d785093a3 100644 --- a/client/config.go +++ b/client/config.go @@ -18,14 +18,11 @@ const ( flagGet = "get" ) -var configDefaults map[string]string - -func init() { - configDefaults = map[string]string{ - "chain-id": "", - "output": "text", - "node": "tcp://localhost:26657", - } +var configDefaults = map[string]string{ + "chain-id": "", + "output": "text", + "node": "tcp://localhost:26657", + "broadcast-mode": "sync", } // ConfigCmd returns a CLI command to interactively create a @@ -56,13 +53,13 @@ func runConfigCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("wrong number of arguments") } - // Load configuration + // load configuration tree, err := loadConfigFile(cfgFile) if err != nil { return err } - // Print the config and exit + // print the config and exit if len(args) == 0 { s, err := tree.ToTomlString() if err != nil { @@ -73,45 +70,54 @@ func runConfigCmd(cmd *cobra.Command, args []string) error { } key := args[0] - // Get value action + + // 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 } - // Set value action 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": + case "chain-id", "output", "node", "broadcast-mode": 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 + // save configuration to disk if err := saveConfigFile(cfgFile, tree); err != nil { return err } - fmt.Fprintf(os.Stderr, "configuration saved to %s\n", cfgFile) + fmt.Fprintf(os.Stderr, "configuration saved to %s\n", cfgFile) return nil } @@ -144,7 +150,7 @@ func loadConfigFile(cfgFile string) (*toml.Tree, error) { } func saveConfigFile(cfgFile string, tree *toml.Tree) error { - fp, err := os.OpenFile(cfgFile, os.O_WRONLY|os.O_CREATE, 0644) + fp, err := os.OpenFile(cfgFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { return err } diff --git a/client/config_test.go b/client/config_test.go new file mode 100644 index 000000000..6263a212a --- /dev/null +++ b/client/config_test.go @@ -0,0 +1,44 @@ +package client + +import ( + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/cli" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +// 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(cli.HomeFlag, 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/broadcast.go b/client/context/broadcast.go index 0503c8e59..4a754a529 100644 --- a/client/context/broadcast.go +++ b/client/context/broadcast.go @@ -3,6 +3,7 @@ package context import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -11,29 +12,36 @@ import ( // an intermediate structure which is logged if the context has a logger // defined. func (ctx CLIContext) BroadcastTx(txBytes []byte) (res sdk.TxResponse, err error) { - if ctx.Async { - if res, err = ctx.BroadcastTxAsync(txBytes); err != nil { - return - } - return + switch ctx.BroadcastMode { + case client.BroadcastSync: + res, err = ctx.BroadcastTxSync(txBytes) + + case client.BroadcastAsync: + res, err = ctx.BroadcastTxAsync(txBytes) + + case client.BroadcastBlock: + res, err = ctx.BroadcastTxCommit(txBytes) + + default: + return sdk.TxResponse{}, fmt.Errorf("unsupported return type %s; supported types: sync, async, block", ctx.BroadcastMode) } - if res, err = ctx.BroadcastTxAndAwaitCommit(txBytes); err != nil { - return - } - - return + return res, err } -// BroadcastTxAndAwaitCommit broadcasts transaction bytes to a Tendermint node -// and waits for a commit. -func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (sdk.TxResponse, error) { +// BroadcastTxCommit broadcasts transaction bytes to a Tendermint node and +// waits for a commit. +// +// 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(tx) + res, err := node.BroadcastTxCommit(txBytes) if err != nil { return sdk.NewResponseFormatBroadcastTxCommit(res), err } @@ -46,35 +54,29 @@ func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (sdk.TxResponse, erro return sdk.NewResponseFormatBroadcastTxCommit(res), fmt.Errorf(res.DeliverTx.Log) } - return sdk.NewResponseFormatBroadcastTxCommit(res), err + return sdk.NewResponseFormatBroadcastTxCommit(res), nil } -// BroadcastTxSync broadcasts transaction bytes to a Tendermint node synchronously. -func (ctx CLIContext) BroadcastTxSync(tx []byte) (sdk.TxResponse, error) { +// 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(tx) - if err != nil { - return sdk.NewResponseFormatBroadcastTx(res), err - } - + res, err := node.BroadcastTxSync(txBytes) return sdk.NewResponseFormatBroadcastTx(res), err } -// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node asynchronously. -func (ctx CLIContext) BroadcastTxAsync(tx []byte) (sdk.TxResponse, error) { +// 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(tx) - if err != nil { - return sdk.NewResponseFormatBroadcastTx(res), err - } - + res, err := node.BroadcastTxAsync(txBytes) return sdk.NewResponseFormatBroadcastTx(res), err } diff --git a/client/context/context.go b/client/context/context.go index beffad0c4..e9d7e6782 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -44,7 +44,7 @@ type CLIContext struct { AccountStore string TrustNode bool UseLedger bool - Async bool + BroadcastMode string PrintResponse bool Verifier tmlite.Verifier VerifierHome string @@ -67,7 +67,8 @@ func NewCLIContext() CLIContext { } from := viper.GetString(client.FlagFrom) - fromAddress, fromName, err := GetFromFields(from) + genOnly := viper.GetBool(client.FlagGenerateOnly) + fromAddress, fromName, err := GetFromFields(from, genOnly) if err != nil { fmt.Printf("failed to get from fields: %v", err) os.Exit(1) @@ -89,11 +90,11 @@ func NewCLIContext() CLIContext { Height: viper.GetInt64(client.FlagHeight), TrustNode: viper.GetBool(client.FlagTrustNode), UseLedger: viper.GetBool(client.FlagUseLedger), - Async: viper.GetBool(client.FlagAsync), + BroadcastMode: viper.GetString(client.FlagBroadcastMode), PrintResponse: viper.GetBool(client.FlagPrintResponse), Verifier: verifier, Simulate: viper.GetBool(client.FlagDryRun), - GenerateOnly: viper.GetBool(client.FlagGenerateOnly), + GenerateOnly: genOnly, FromAddress: fromAddress, FromName: fromName, Indent: viper.GetBool(client.FlagIndentResponse), @@ -247,6 +248,13 @@ func (ctx CLIContext) WithFromAddress(addr sdk.AccAddress) CLIContext { 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 @@ -259,7 +267,7 @@ func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) { case "json": if ctx.Indent { - out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ") + out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ") } else { out, err = ctx.Codec.MarshalJSON(toPrint) } @@ -274,12 +282,22 @@ func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) { } // GetFromFields returns a from account address and Keybase name given either -// an address or key name. -func GetFromFields(from string) (sdk.AccAddress, string, error) { +// an address or key name. If genOnly is true, only a valid Bech32 cosmos +// address is returned. +func GetFromFields(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, "", err + } + + return addr, "", nil + } + keybase, err := keys.NewKeyBaseFromHomeFlag() if err != nil { return nil, "", err diff --git a/client/flags.go b/client/flags.go index 62d912908..77aec7e24 100644 --- a/client/flags.go +++ b/client/flags.go @@ -18,11 +18,20 @@ const ( DefaultGasLimit = 200000 GasFlagAuto = "auto" + // BroadcastBlock defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BroadcastBlock = "block" + // BroadcastSync defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BroadcastSync = "sync" + // BroadcastAsync defines a tx broadcasting mode where the client returns + // immediately. + BroadcastAsync = "async" + FlagUseLedger = "ledger" FlagChainID = "chain-id" FlagNode = "node" FlagHeight = "height" - FlagGas = "gas" FlagGasAdjustment = "gas-adjustment" FlagTrustNode = "trust-node" FlagFrom = "from" @@ -32,7 +41,7 @@ const ( FlagMemo = "memo" FlagFees = "fees" FlagGasPrices = "gas-prices" - FlagAsync = "async" + FlagBroadcastMode = "broadcast-mode" FlagPrintResponse = "print-response" FlagDryRun = "dry-run" FlagGenerateOnly = "generate-only" @@ -40,10 +49,6 @@ const ( FlagListenAddr = "laddr" FlagCORS = "cors" FlagMaxOpenConnections = "max-open" - FlagTLS = "tls" - FlagSSLHosts = "ssl-hosts" - FlagSSLCertFile = "ssl-certfile" - FlagSSLKeyFile = "ssl-keyfile" FlagOutputDocument = "output-document" // inspired by wget -O FlagSkipConfirmation = "yes" ) @@ -62,7 +67,6 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { 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, "block height to query, omit to get most recent provable block") viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) @@ -77,15 +81,15 @@ 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().Uint64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") - c.Flags().Uint64(FlagSequence, 0, "Sequence number to sign the tx") + 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: 10stake,1atom") - c.Flags().String(FlagGasPrices, "", "Gas prices to determine the transaction fee (e.g. 0.00001stake)") + 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().Bool(FlagAsync, false, "broadcast transactions asynchronously") + c.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") 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") @@ -111,10 +115,6 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { 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().Bool(FlagTLS, false, "Enable SSL/TLS layer") - cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") - cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") - cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") cmd.Flags().String(FlagCORS, "", "Set the domains that can make CORS requests (* for all)") cmd.Flags().Int(FlagMaxOpenConnections, 1000, "The number of maximum open connections") diff --git a/client/keys/add.go b/client/keys/add.go index c11316d1d..7ab5ecdb8 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -74,6 +74,7 @@ the flag --nosort is set. cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation") cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") + cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") return cmd } @@ -146,7 +147,7 @@ func runAddCmd(_ *cobra.Command, args []string) error { return err } - fmt.Fprintf(os.Stderr, "Key %q saved to disk.", name) + fmt.Fprintf(os.Stderr, "Key %q saved to disk.\n", name) return nil } @@ -216,7 +217,7 @@ func runAddCmd(_ *cobra.Command, args []string) error { } if !bip39.IsMnemonicValid(mnemonic) { - fmt.Fprintf(os.Stderr, "Error: Mnemonic is not valid") + fmt.Fprintf(os.Stderr, "Error: Mnemonic is not valid.\n") return nil } diff --git a/client/keys/list.go b/client/keys/list.go index db4cf0fe2..19873aabb 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -1,17 +1,20 @@ package keys import ( + "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" ) func listKeysCmd() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "list", Short: "List all keys", Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, RunE: runListCmd, } + cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") + return cmd } func runListCmd(cmd *cobra.Command, args []string) error { diff --git a/client/keys/show.go b/client/keys/show.go index e1d253cd3..eaa800e11 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -3,6 +3,7 @@ package keys import ( "errors" "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/crypto" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -49,6 +50,7 @@ consisting of all the keys provided by name and multisig threshold.`, cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in the device") cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures") cmd.Flags().BoolP(flagShowMultiSig, "m", false, "Output multisig pubkey constituents, threshold, and weights") + cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") return cmd } diff --git a/client/keys/types.go b/client/keys/types.go index 75ecabe83..079ef4962 100644 --- a/client/keys/types.go +++ b/client/keys/types.go @@ -11,6 +11,17 @@ type AddNewKey struct { Index int `json:"index,string,omitempty"` } +// NewAddNewKey constructs a new AddNewKey request structure. +func NewAddNewKey(name, password, mnemonic string, account, index int) AddNewKey { + return AddNewKey{ + Name: name, + Password: password, + Mnemonic: mnemonic, + Account: account, + Index: index, + } +} + // RecoverKeyBody recovers a key type RecoverKey struct { Password string `json:"password"` @@ -19,13 +30,26 @@ type RecoverKey struct { Index int `json:"index,string,omitempty"` } +// NewRecoverKey constructs a new RecoverKey request structure. +func NewRecoverKey(password, mnemonic string, account, index int) RecoverKey { + return RecoverKey{Password: password, Mnemonic: mnemonic, Account: account, Index: index} +} + // UpdateKeyReq requests updating a key type UpdateKeyReq struct { OldPassword string `json:"old_password"` NewPassword string `json:"new_password"` } +// NewUpdateKeyReq constructs a new UpdateKeyReq structure. +func NewUpdateKeyReq(old, new string) UpdateKeyReq { + return UpdateKeyReq{OldPassword: old, NewPassword: new} +} + // DeleteKeyReq requests deleting a key type DeleteKeyReq struct { Password string `json:"password"` } + +// NewDeleteKeyReq constructs a new DeleteKeyReq structure. +func NewDeleteKeyReq(password string) DeleteKeyReq { return DeleteKeyReq{Password: password} } diff --git a/client/keys/utils.go b/client/keys/utils.go index 5391e704f..c86161038 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -118,8 +118,14 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) { printKeyTextHeader() printKeyOutput(ko) - case "json": - out, err := MarshalJSON(ko) + case OutputFormatJSON: + var out []byte + var err error + if viper.GetBool(client.FlagIndentResponse) { + out, err = cdc.MarshalJSONIndent(ko, "", " ") + } else { + out, err = cdc.MarshalJSON(ko) + } if err != nil { panic(err) } @@ -142,11 +148,18 @@ func printInfos(infos []keys.Info) { } case OutputFormatJSON: - out, err := MarshalJSON(kos) + var out []byte + var err error + + if viper.GetBool(client.FlagIndentResponse) { + out, err = cdc.MarshalJSONIndent(kos, "", " ") + } else { + out, err = cdc.MarshalJSON(kos) + } + if err != nil { panic(err) } - fmt.Println(string(out)) } } diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go deleted file mode 100644 index 0ec527e13..000000000 --- a/client/lcd/certificates.go +++ /dev/null @@ -1,175 +0,0 @@ -package lcd - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "math/big" - "net" - "os" - "strings" - "time" -) - -// default: 30 days -const defaultValidFor = 30 * 24 * time.Hour - -func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateKey, err error) { - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - notBefore := time.Now() - notAfter := notBefore.Add(defaultValidFor) - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - err = fmt.Errorf("failed to generate serial number: %s", err) - return - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Gaia Lite"}, - }, - DNSNames: []string{"localhost"}, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - } - hosts := strings.Split(host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } - - certBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) - if err != nil { - err = fmt.Errorf("couldn't create certificate: %s", err) - return - } - return -} - -func writeCertAndPrivKey(certBytes []byte, priv *ecdsa.PrivateKey) (certFile string, keyFile string, err error) { - if priv == nil { - err = errors.New("private key is nil") - return - } - certFile, err = writeCertificateFile(certBytes) - if err != nil { - return - } - keyFile, err = writeKeyFile(priv) - return -} - -func writeCertificateFile(certBytes []byte) (filename string, err error) { - f, err := ioutil.TempFile("", "cert_") - if err != nil { - return - } - defer f.Close() - filename = f.Name() - if err := pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { - return filename, fmt.Errorf("failed to write data to %s: %s", filename, err) - } - return -} - -func writeKeyFile(priv *ecdsa.PrivateKey) (filename string, err error) { - f, err := ioutil.TempFile("", "key_") - if err != nil { - return - } - defer f.Close() - filename = f.Name() - block, err := pemBlockForKey(priv) - if err != nil { - return - } - if err := pem.Encode(f, block); err != nil { - return filename, fmt.Errorf("failed to write data to %s: %s", filename, err) - } - return -} - -func pemBlockForKey(priv *ecdsa.PrivateKey) (*pem.Block, error) { - b, err := x509.MarshalECPrivateKey(priv) - if err != nil { - return nil, fmt.Errorf("unable to marshal ECDSA private key: %v", err) - } - return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil - -} - -func genCertKeyFilesAndReturnFingerprint(sslHosts string) (certFile, keyFile string, fingerprint string, err error) { - certBytes, priv, err := generateSelfSignedCert(sslHosts) - if err != nil { - return - } - certFile, keyFile, err = writeCertAndPrivKey(certBytes, priv) - cleanupFunc := func() { - os.Remove(certFile) - os.Remove(keyFile) - } - // Either of the files could have been written already, - // thus clean up regardless of the error. - if err != nil { - defer cleanupFunc() - return - } - fingerprint, err = fingerprintForCertificate(certBytes) - if err != nil { - defer cleanupFunc() - return - } - return -} - -func fingerprintForCertificate(certBytes []byte) (string, error) { - cert, err := x509.ParseCertificate(certBytes) - if err != nil { - return "", err - } - h := sha256.New() - h.Write(cert.Raw) - fingerprintBytes := h.Sum(nil) - var buf bytes.Buffer - for i, b := range fingerprintBytes { - if i > 0 { - fmt.Fprintf(&buf, ":") - } - fmt.Fprintf(&buf, "%02X", b) - } - return fmt.Sprintf("SHA256 Fingerprint=%s", buf.String()), nil -} - -func fingerprintFromFile(certFile string) (string, error) { - f, err := os.Open(certFile) - if err != nil { - return "", err - } - defer f.Close() - data, err := ioutil.ReadAll(f) - if err != nil { - return "", err - } - block, _ := pem.Decode(data) - if block == nil { - return "", fmt.Errorf("couldn't find PEM data in %s", certFile) - } - return fingerprintForCertificate(block.Bytes) -} diff --git a/client/lcd/certificates_test.go b/client/lcd/certificates_test.go deleted file mode 100644 index 3f48c194c..000000000 --- a/client/lcd/certificates_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package lcd - -import ( - "crypto/ecdsa" - "crypto/x509" - "io/ioutil" - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestGenerateSelfSignedCert(t *testing.T) { - host := "127.0.0.1,localhost,::1" - certBytes, _, err := generateSelfSignedCert(host) - require.Nil(t, err) - cert, err := x509.ParseCertificate(certBytes) - require.Nil(t, err) - require.Equal(t, 2, len(cert.IPAddresses)) - require.Equal(t, 2, len(cert.DNSNames)) - require.True(t, cert.IsCA) -} - -func TestWriteCertAndPrivKey(t *testing.T) { - expectedPerm := "-rw-------" - derBytes, priv, err := generateSelfSignedCert("localhost") - require.Nil(t, err) - type args struct { - certBytes []byte - priv *ecdsa.PrivateKey - } - tests := []struct { - name string - args args - wantErr bool - }{ - {"valid certificate", args{derBytes, priv}, false}, - {"garbage", args{[]byte("some garbage"), nil}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotCertFile, gotKeyFile, err := writeCertAndPrivKey(tt.args.certBytes, tt.args.priv) - defer os.Remove(gotCertFile) - defer os.Remove(gotKeyFile) - if tt.wantErr { - require.NotNil(t, err) - return - } - require.Nil(t, err) - info, err := os.Stat(gotCertFile) - require.Nil(t, err) - require.True(t, info.Mode().IsRegular()) - require.Equal(t, expectedPerm, info.Mode().String()) - info, err = os.Stat(gotKeyFile) - require.Nil(t, err) - require.True(t, info.Mode().IsRegular()) - require.Equal(t, expectedPerm, info.Mode().String()) - }) - } -} - -func TestFingerprintFromFile(t *testing.T) { - cert := `-----BEGIN CERTIFICATE----- -MIIBbDCCARGgAwIBAgIQSuFKYv/22v+cxtVgMUrQADAKBggqhkjOPQQDAjASMRAw -DgYDVQQKEwdBY21lIENvMB4XDTE4MDkyMDIzNDQyNloXDTE5MDkyMDIzNDQyNlow -EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDIo -ujAesRczcPVAWiLhpeV1B7hS/RI2LJaGj3QjyJ8hiUthJTPIamr8m7LuS/U5fS0o -hY297YeTIGo9YkxClICjSTBHMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr -BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MA8GA1UdEQQIMAaHBH8AAAEwCgYIKoZI -zj0EAwIDSQAwRgIhAKnwbhX9FrGG1otCVLwhClQ3RaLxnNpCgIGTqSimb34cAiEA -stMN+IqMCKWlZyGqxGIiyksMLMEU3lRqKNQn2EoAZJY= ------END CERTIFICATE-----` - wantFingerprint := `SHA256 Fingerprint=0B:ED:9A:AA:A2:D1:7E:B2:53:56:F6:FC:C0:E6:1A:69:70:21:A2:B0:90:FC:AF:BB:EF:AE:2C:78:52:AB:68:40` - certFile, err := ioutil.TempFile("", "test_cert_") - require.Nil(t, err) - _, err = certFile.Write([]byte(cert)) - require.Nil(t, err) - err = certFile.Close() - require.Nil(t, err) - defer os.Remove(certFile.Name()) - fingerprint, err := fingerprintFromFile(certFile.Name()) - require.Nil(t, err) - require.Equal(t, wantFingerprint, fingerprint) - - // test failure - emptyFile, err := ioutil.TempFile("", "test_cert_") - require.Nil(t, err) - err = emptyFile.Close() - require.Nil(t, err) - defer os.Remove(emptyFile.Name()) - _, err = fingerprintFromFile(emptyFile.Name()) - require.NotNil(t, err) -} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 88845a731..55c773cfa 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client" @@ -520,9 +522,18 @@ func TestBonding(t *testing.T) { // test redelegation rdTokens := sdk.TokensFromTendermintPower(30) resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], rdTokens, fees) + require.Equal(t, uint32(0), resultTx.Code) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.Code) + // query delegations, unbondings and redelegations from validator and delegator + delegatorDels = getDelegatorDelegations(t, port, addr) + require.Len(t, delegatorDels, 1) + require.Equal(t, operAddrs[1], delegatorDels[0].ValidatorAddress) + + // TODO uncomment once all validators actually sign in the lcd tests + //validator2 := getValidator(t, port, operAddrs[1]) + //delTokensAfterRedelegation := validator2.ShareTokens(delegatorDels[0].GetShares()) + //require.Equal(t, rdTokens.ToDec(), delTokensAfterRedelegation) // verify balance after paying fees acc = getAccount(t, port, addr) @@ -542,20 +553,6 @@ func TestBonding(t *testing.T) { require.Len(t, txs, 1) require.Equal(t, resultTx.Height, txs[0].Height) - // query delegations, unbondings and redelegations from validator and delegator - delegatorDels = getDelegatorDelegations(t, port, addr) - require.Len(t, delegatorDels, 1) - require.Equal(t, operAddrs[1], delegatorDels[0].ValidatorAddress) - - // because the second validator never signs during these tests, if this - // this test takes a long time to run, eventually this second validator - // will get slashed, meaning that it's exchange rate is no-longer 1-to-1, - // hence we utilize the exchange rate in the following test - - validator2 := getValidator(t, port, operAddrs[1]) - delTokensAfterRedelegation := validator2.ShareTokens(delegatorDels[0].GetShares()) - require.Equal(t, rdTokens.ToDec(), delTokensAfterRedelegation) - redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1]) require.Len(t, redelegation, 1) require.Len(t, redelegation[0].Entries, 1) @@ -614,7 +611,9 @@ func TestSubmitProposal(t *testing.T) { require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) + bz, err := hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -649,7 +648,9 @@ func TestDeposit(t *testing.T) { require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) + bz, err := hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -680,7 +681,7 @@ func TestDeposit(t *testing.T) { // query proposal totalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(10))} proposal = getProposal(t, port, proposalID) - require.True(t, proposal.GetTotalDeposit().IsEqual(totalCoins)) + require.True(t, proposal.TotalDeposit.IsEqual(totalCoins)) // query deposit deposit := getDeposit(t, port, proposalID, addr) @@ -706,7 +707,9 @@ func TestVote(t *testing.T) { require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) + bz, err := hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -718,7 +721,7 @@ func TestVote(t *testing.T) { // query proposal proposal := getProposal(t, port, proposalID) require.Equal(t, "Test", proposal.GetTitle()) - require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus()) + require.Equal(t, gov.StatusVotingPeriod, proposal.Status) // vote resultTx = doVote(t, port, seed, name1, pw, addr, proposalID, "Yes", fees) @@ -787,6 +790,8 @@ func TestUnjail(t *testing.T) { require.Equal(t, true, signingInfo.IndexOffset > 0) require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil) require.Equal(t, true, signingInfo.MissedBlocksCounter == 0) + signingInfoList := getSigningInfoList(t, port) + require.NotZero(t, len(signingInfoList)) } func TestProposalsQuery(t *testing.T) { @@ -805,18 +810,24 @@ func TestProposalsQuery(t *testing.T) { // Addr1 proposes (and deposits) proposals #1 and #2 resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit, fees) var proposalID1 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID1) + bz, err := hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID1) tests.WaitForHeight(resultTx.Height+1, port) resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit, fees) var proposalID2 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID2) + bz, err = hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID2) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 proposes (and deposits) proposals #3 resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], halfMinDeposit, fees) var proposalID3 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID3) + bz, err = hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID3) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 deposits on proposals #2 & #3 @@ -855,13 +866,13 @@ func TestProposalsQuery(t *testing.T) { // Only proposals #1 should be in Deposit Period proposals := getProposalsFilterStatus(t, port, gov.StatusDepositPeriod) require.Len(t, proposals, 1) - require.Equal(t, proposalID1, proposals[0].GetProposalID()) + require.Equal(t, proposalID1, proposals[0].ProposalID) // Only proposals #2 and #3 should be in Voting Period proposals = getProposalsFilterStatus(t, port, gov.StatusVotingPeriod) require.Len(t, proposals, 2) - require.Equal(t, proposalID2, proposals[0].GetProposalID()) - require.Equal(t, proposalID3, proposals[1].GetProposalID()) + require.Equal(t, proposalID2, proposals[0].ProposalID) + require.Equal(t, proposalID3, proposals[1].ProposalID) // Addr1 votes on proposals #2 & #3 resultTx = doVote(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID2, "Yes", fees) @@ -875,31 +886,31 @@ func TestProposalsQuery(t *testing.T) { // Test query all proposals proposals = getProposalsAll(t, port) - require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) - require.Equal(t, proposalID2, (proposals[1]).GetProposalID()) - require.Equal(t, proposalID3, (proposals[2]).GetProposalID()) + require.Equal(t, proposalID1, (proposals[0]).ProposalID) + require.Equal(t, proposalID2, (proposals[1]).ProposalID) + require.Equal(t, proposalID3, (proposals[2]).ProposalID) // Test query deposited by addr1 proposals = getProposalsFilterDepositor(t, port, addrs[0]) - require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID1, (proposals[0]).ProposalID) // Test query deposited by addr2 proposals = getProposalsFilterDepositor(t, port, addrs[1]) - require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) - require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) + require.Equal(t, proposalID2, (proposals[0]).ProposalID) + require.Equal(t, proposalID3, (proposals[1]).ProposalID) // Test query voted by addr1 proposals = getProposalsFilterVoter(t, port, addrs[0]) - require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) - require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) + require.Equal(t, proposalID2, (proposals[0]).ProposalID) + require.Equal(t, proposalID3, (proposals[1]).ProposalID) // Test query voted by addr2 proposals = getProposalsFilterVoter(t, port, addrs[1]) - require.Equal(t, proposalID3, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID3, (proposals[0]).ProposalID) // Test query voted and deposited by addr1 proposals = getProposalsFilterVoterDepositor(t, port, addrs[0], addrs[0]) - require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID2, (proposals[0]).ProposalID) // Test query votes on Proposal 2 votes := getVotes(t, port, proposalID2) @@ -1008,3 +1019,29 @@ func TestDistributionFlow(t *testing.T) { resultTx = doWithdrawDelegatorAllRewards(t, port, seed, name1, pw, addr, fees) require.Equal(t, uint32(0), resultTx.Code) } + +func TestMintingQueries(t *testing.T) { + kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, "")) + require.NoError(t, err) + addr, _ := CreateAddr(t, name1, pw, kb) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) + defer cleanup() + + res, body := Request(t, port, "GET", "/minting/parameters", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var params mint.Params + require.NoError(t, cdc.UnmarshalJSON([]byte(body), ¶ms)) + + res, body = Request(t, port, "GET", "/minting/inflation", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var inflation sdk.Dec + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &inflation)) + + res, body = Request(t, port, "GET", "/minting/annual-provisions", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var annualProvisions sdk.Dec + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &annualProvisions)) +} diff --git a/client/lcd/root.go b/client/lcd/root.go index 0f6fb120a..3a5144a2a 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -52,61 +52,27 @@ func NewRestServer(cdc *codec.Codec) *RestServer { } // Start starts the rest server -func (rs *RestServer) Start(listenAddr string, sslHosts string, - certFile string, keyFile string, maxOpen int, secure bool) (err error) { - +func (rs *RestServer) Start(listenAddr string, maxOpen int) (err error) { server.TrapSignal(func() { err := rs.listener.Close() rs.log.Error("error closing listener", "err", err) }) - rs.listener, err = rpcserver.Listen( - listenAddr, - rpcserver.Config{MaxOpenConnections: maxOpen}, - ) + cfg := rpcserver.DefaultConfig() + cfg.MaxOpenConnections = maxOpen + + rs.listener, err = rpcserver.Listen(listenAddr, cfg) if err != nil { return } - rs.log.Info(fmt.Sprintf("Starting Gaia Lite REST service (chain-id: %q)...", - viper.GetString(client.FlagChainID))) - - // launch rest-server in insecure mode - if !secure { - return rpcserver.StartHTTPServer(rs.listener, rs.Mux, rs.log) - } - - // handle certificates - if certFile != "" { - // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 - if err := validateCertKeyFiles(certFile, keyFile); err != nil { - return err - } - - // cert/key pair is provided, read the fingerprint - rs.fingerprint, err = fingerprintFromFile(certFile) - if err != nil { - return err - } - } else { - // if certificate is not supplied, generate a self-signed one - certFile, keyFile, rs.fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) - if err != nil { - return err - } - - defer func() { - os.Remove(certFile) - os.Remove(keyFile) - }() - } - - rs.log.Info(rs.fingerprint) - return rpcserver.StartHTTPAndTLSServer( - rs.listener, - rs.Mux, - certFile, keyFile, - rs.log, + rs.log.Info( + fmt.Sprintf( + "Starting Gaia Lite REST service (chain-id: %q)...", + viper.GetString(client.FlagChainID), + ), ) + + return rpcserver.StartHTTPServer(rs.listener, rs.Mux, rs.log, cfg) } // ServeCommand will start a Gaia Lite REST service as a blocking process. It @@ -122,13 +88,8 @@ func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.C registerRoutesFn(rs) // Start the rest server and return error if one exists - err = rs.Start( - viper.GetString(client.FlagListenAddr), - viper.GetString(client.FlagSSLHosts), - viper.GetString(client.FlagSSLCertFile), - viper.GetString(client.FlagSSLKeyFile), - viper.GetInt(client.FlagMaxOpenConnections), - viper.GetBool(client.FlagTLS)) + err = rs.Start(viper.GetString(client.FlagListenAddr), + viper.GetInt(client.FlagMaxOpenConnections)) return err }, diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 0c6b78da8..cb479a63f 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -21,7 +21,7 @@ tags: description: Query app version schemes: - https -host: fabo.interblock.io:1317 +host: stargate.cosmos.network securityDefinitions: kms: type: basic @@ -65,6 +65,17 @@ paths: 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 @@ -79,9 +90,14 @@ paths: example: 0.15.0 other: description: more information on versions - type: array - items: - type: string + 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: @@ -122,6 +138,7 @@ paths: description: Block height required: true type: number + x-example: 1 responses: 200: description: The block at a specific height @@ -167,6 +184,7 @@ paths: description: Block height required: true type: number + x-example: 1 responses: 200: description: The validator set at a specific block height @@ -198,6 +216,7 @@ paths: description: Tx hash required: true type: string + x-example: 88D6B85EAB87D43CDF50F39C22FC2237A37FEDC4CE723200AD0AF48CBEDBC317 responses: 200: description: Tx with the provided hash @@ -219,14 +238,17 @@ paths: type: string description: "transaction tags such as 'action=submit-proposal' and 'proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc' which results in the following endpoint: 'GET /txs?action=submit-proposal&proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'" required: true + x-example: 'TODO' - in: query name: page - description: Pagination page + description: Page number type: integer + x-example: 1 - in: query - name: size - description: Pagination size + name: limit + description: Maximum number of items per page type: integer + x-example: 1 responses: 200: description: All txs matching the provided tags @@ -250,14 +272,14 @@ paths: parameters: - in: body name: txBroadcast - description: The tx must be a signed StdTx. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away). + 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" - return: + mode: type: string example: block responses: @@ -313,6 +335,7 @@ paths: description: Account address in bech32 format required: true type: string + x-example: cosmos16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt responses: 200: description: Account balances @@ -339,6 +362,7 @@ paths: description: Account address in bech32 format required: true type: string + x-example: cosmos16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt - in: body name: account description: The sender and tx information @@ -374,6 +398,7 @@ paths: description: Account address required: true type: string + x-example: cosmos16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt responses: 200: description: Account information on the blockchain @@ -408,6 +433,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf get: summary: Get all delegations from a delegator tags: @@ -466,11 +492,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Query the current delegation between a delegator and a validator tags: @@ -493,6 +521,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf get: summary: Get all unbonding delegations from a delegator tags: @@ -552,11 +581,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Query all unbonding delegations between a delegator and a validator tags: @@ -613,6 +644,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf post: summary: Submit a redelegation parameters: @@ -655,6 +687,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf get: summary: Query all validators that a delegator is bonded to tags: @@ -679,11 +712,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - in: path name: validatorAddr description: Bech32 ValAddress of Delegator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Query a validator that a delegator is bonded to tags: @@ -706,6 +741,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf get: summary: Get all staking txs (i.e msgs) from a delegator tags: @@ -748,6 +784,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Query the information from a single validator tags: @@ -770,6 +807,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Get all delegations from a validator tags: @@ -794,6 +832,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Get all unbonding delegations from a validator tags: @@ -881,26 +920,52 @@ paths: name: validatorPubKey required: true in: path + x-example: cosmosvalconspub1zcjduepq7mft6gfls57a0a42d7uhx656cckhfvtrlmw744jv4q0mvlv0dypskehfk8 responses: 200: description: OK schema: - type: object - properties: - start_height: - type: string - index_offset: - type: string - jailed_until: - type: string - missed_blocks_counter: - type: string + $ref: "#/definitions/SigningInfo" 204: description: No sign info of this validator 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: + - ICS23 + 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" + 204: + description: No validators with sign info + 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 @@ -917,6 +982,7 @@ paths: name: validatorAddr required: true in: path + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys - description: "" name: UnjailBody in: body @@ -1053,6 +1119,7 @@ paths: name: proposalId required: true in: path + x-example: '1' responses: 200: description: OK @@ -1075,6 +1142,7 @@ paths: name: proposalId required: true in: path + x-example: '1' responses: 200: description: OK @@ -1097,6 +1165,7 @@ paths: name: proposalId required: true in: path + x-example: '1' responses: 200: description: OK @@ -1123,6 +1192,7 @@ paths: name: proposalId required: true in: path + x-example: '1' - description: "" name: post_deposit_body in: body @@ -1163,11 +1233,13 @@ paths: name: proposalId required: true in: path + x-example: '1' - type: string description: Bech32 depositor address name: depositor required: true in: path + x-example: cosmos1xl6453f6q6dv5770c9ue6hspdc0vxfuqtudkhz responses: 200: description: OK @@ -1193,6 +1265,7 @@ paths: name: proposalId required: true in: path + x-example: '1' responses: 200: description: OK @@ -1219,6 +1292,7 @@ paths: name: proposalId required: true in: path + x-example: '1' - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` name: post_vote_body in: body @@ -1258,11 +1332,13 @@ paths: name: proposalId required: true in: path + x-example: '1' - type: string description: Bech32 voter address name: voter required: true in: path + x-example: cosmos1qwl879nx9t6kef4supyazayf7vjhennyjqwjgr responses: 200: description: OK @@ -1288,6 +1364,7 @@ paths: name: proposalId required: true in: path + x-example: '1' responses: 200: description: OK @@ -1381,6 +1458,7 @@ paths: 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 @@ -1433,11 +1511,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Query a delegation reward description: Query a single delegation reward by a delegator @@ -1490,6 +1570,7 @@ paths: 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 @@ -1542,6 +1623,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Validator distribution information description: Query the distribution information of a single validator @@ -1565,21 +1647,22 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: - summary: Fee distribution outstanding rewards of a single validator - tags: - - ICS24 - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Coin" - 500: - description: Internal Server Error + summary: Fee distribution outstanding rewards of a single validator + tags: + - ICS24 + 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 @@ -1587,6 +1670,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string + x-example: cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys get: summary: Commission and self-delegation rewards of a single validator description: Query the commission and self-delegation rewards of validator. @@ -1632,6 +1716,22 @@ paths: description: Key password is wrong 500: description: Internal Server Error + /distribution/community_pool: + get: + summary: Community pool parameters + tags: + - ICS24 + 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 @@ -1652,6 +1752,54 @@ paths: type: string 500: description: Internal Server Error + /minting/parameters: + get: + summary: Minting module parameters + 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: integer + 500: + description: Internal Server Error + /minting/inflation: + get: + summary: Current minting inflation value + 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 + produces: + - application/json + responses: + 200: + description: OK + schema: + type: string + 500: + description: Internal Server Error definitions: CheckTxResult: type: object @@ -1744,7 +1892,7 @@ definitions: properties: denom: type: string - example: steak + example: stake amount: type: string example: "50" @@ -1882,6 +2030,15 @@ definitions: $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: @@ -1980,7 +2137,7 @@ definitions: pub_key: type: string example: cosmosvalconspub1zcjduepq7sjfglw7ra4mjxpw4ph7dtdhdheh7nz8dfgl6t8u2n5szuuql9mqsrwquu - power: + voting_power: type: string example: "1000" proposer_priority: @@ -2165,3 +2322,14 @@ definitions: type: array items: $ref: "#/definitions/Coin" + SigningInfo: + type: object + properties: + start_height: + type: string + index_offset: + type: string + jailed_until: + type: string + missed_blocks_counter: + type: string diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 507c6af03..856efaf95 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -42,6 +42,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + mintrest "github.com/cosmos/cosmos-sdk/x/mint/client/rest" "github.com/cosmos/cosmos-sdk/x/slashing" slashingrest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" "github.com/cosmos/cosmos-sdk/x/staking" @@ -198,7 +199,7 @@ func InitClientHome(t *testing.T, dir string) string { // TODO: Make InitializeTestLCD safe to call in multiple tests at the same time // InitializeTestLCD starts Tendermint and the LCD in process, listening on // their respective sockets where nValidators is the total number of validators -// and initAddrs are the accounts to initialize with some steak tokens. It +// and initAddrs are the accounts to initialize with some stake tokens. It // returns a cleanup function, a set of validator public keys, and a port. func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress, minting bool) ( cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) { @@ -220,14 +221,14 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress privVal.Reset() db := dbm.NewMemDB() - app := gapp.NewGaiaApp(logger, db, nil, true) + app := gapp.NewGaiaApp(logger, db, nil, true, false) cdc = gapp.MakeCodec() genesisFile := config.GenesisFile() genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) require.Nil(t, err) genDoc.Validators = nil - genDoc.SaveAs(genesisFile) + require.NoError(t, genDoc.SaveAs(genesisFile)) genTxs := []json.RawMessage{} // append any additional (non-proposing) validators @@ -299,6 +300,9 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress genesisState.MintData.Minter.Inflation = inflationMin genesisState.MintData.Params.InflationMin = inflationMin + // initialize crisis data + genesisState.CrisisData.ConstantFee = sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000) + // double check inflation is set according to the minting boolean flag if minting { require.Equal(t, sdk.MustNewDecFromStr("15000.0"), @@ -337,7 +341,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress cleanup = func() { logger.Debug("cleaning up LCD initialization") - node.Stop() + node.Stop() //nolint:errcheck node.Wait() lcd.Close() } @@ -390,11 +394,11 @@ func startTM( func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing.T) (net.Listener, error) { rs := NewRestServer(cdc) registerRoutes(rs) - listener, err := tmrpc.Listen(listenAddr, tmrpc.Config{}) + listener, err := tmrpc.Listen(listenAddr, tmrpc.DefaultConfig()) if err != nil { return nil, err } - go tmrpc.StartHTTPServer(listener, rs.Mux, logger) + go tmrpc.StartHTTPServer(listener, rs.Mux, logger, tmrpc.DefaultConfig()) //nolint:errcheck return listener, nil } @@ -408,6 +412,7 @@ func registerRoutes(rs *RestServer) { stakingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) govrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + mintrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } // Request makes a test LCD test request. It returns a response object and a @@ -559,7 +564,7 @@ func getKeys(t *testing.T, port string) []keys.KeyOutput { // POST /keys Create a new account locally func doKeysPost(t *testing.T, port, name, password, mnemonic string, account int, index int) keys.KeyOutput { - pk := clientkeys.AddNewKey{name, password, mnemonic, account, index} + pk := clientkeys.NewAddNewKey(name, password, mnemonic, account, index) req, err := cdc.MarshalJSON(pk) require.NoError(t, err) @@ -585,7 +590,7 @@ func getKeysSeed(t *testing.T, port string) string { // POST /keys/{name}/recove Recover a account from a seed func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, mnemonic string, account uint32, index uint32) { - pk := clientkeys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)} + pk := clientkeys.NewRecoverKey(recoverPassword, mnemonic, int(account), int(index)) req, err := cdc.MarshalJSON(pk) require.NoError(t, err) @@ -613,7 +618,7 @@ func getKey(t *testing.T, port, name string) keys.KeyOutput { // PUT /keys/{name} Update the password for this account in the KMS func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) { - kr := clientkeys.UpdateKeyReq{oldPassword, newPassword} + kr := clientkeys.NewUpdateKeyReq(oldPassword, newPassword) req, err := cdc.MarshalJSON(kr) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) @@ -627,7 +632,7 @@ func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail b // DELETE /keys/{name} Remove an account func deleteKey(t *testing.T, port, name, password string) { - dk := clientkeys.DeleteKeyReq{password} + dk := clientkeys.NewDeleteKeyReq(password) req, err := cdc.MarshalJSON(dk) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) @@ -651,7 +656,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { // POST /tx/broadcast Send a signed Tx func doBroadcast(t *testing.T, port string, tx auth.StdTx) (*http.Response, string) { - txReq := clienttx.BroadcastReq{Tx: tx, Return: "block"} + txReq := clienttx.BroadcastReq{Tx: tx, Mode: "block"} req, err := cdc.MarshalJSON(txReq) require.Nil(t, err) @@ -668,7 +673,7 @@ func doTransfer( resp, body, recvAddr := doTransferWithGas( t, port, seed, name, memo, pwd, addr, "", 1.0, false, true, fees, ) - require.Equal(t, http.StatusOK, resp.StatusCode, resp) + require.Equal(t, http.StatusOK, resp.StatusCode, body) var txResp sdk.TxResponse err := cdc.UnmarshalJSON([]byte(body), &txResp) @@ -815,11 +820,11 @@ func doDelegate( from := acc.GetAddress().String() baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false) - msg := msgDelegationsInput{ + msg := stakingrest.DelegateRequest{ BaseReq: baseReq, DelegatorAddress: delAddr, ValidatorAddress: valAddr, - Delegation: sdk.NewCoin(sdk.DefaultBondDenom, amount), + Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount), } req, err := cdc.MarshalJSON(msg) @@ -839,13 +844,6 @@ func doDelegate( return txResp } -type msgDelegationsInput struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32 - Delegation sdk.Coin `json:"delegation"` -} - // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doUndelegate( t *testing.T, port, name, pwd string, delAddr sdk.AccAddress, @@ -859,11 +857,11 @@ func doUndelegate( from := acc.GetAddress().String() baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false) - msg := msgUndelegateInput{ + msg := stakingrest.UndelegateRequest{ BaseReq: baseReq, DelegatorAddress: delAddr, ValidatorAddress: valAddr, - SharesAmount: amount.ToDec(), + Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount), } req, err := cdc.MarshalJSON(msg) @@ -882,13 +880,6 @@ func doUndelegate( return txResp } -type msgUndelegateInput struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32 - SharesAmount sdk.Dec `json:"shares"` -} - // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doBeginRedelegation( t *testing.T, port, name, pwd string, delAddr sdk.AccAddress, valSrcAddr, @@ -902,12 +893,12 @@ func doBeginRedelegation( from := acc.GetAddress().String() baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false) - msg := stakingrest.MsgBeginRedelegateInput{ + msg := stakingrest.RedelegateRequest{ BaseReq: baseReq, DelegatorAddress: delAddr, ValidatorSrcAddress: valSrcAddr, ValidatorDstAddress: valDstAddr, - SharesAmount: amount.ToDec(), + Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount), } req, err := cdc.MarshalJSON(msg) @@ -926,14 +917,6 @@ func doBeginRedelegation( return txResp } -type msgBeginRedelegateInput struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` // in bech32 - ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` // in bech32 - SharesAmount sdk.Dec `json:"shares"` -} - // GET /staking/delegators/{delegatorAddr}/delegations Get all delegations from a delegator func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []staking.Delegation { res, body := Request(t, port, "GET", fmt.Sprintf("/staking/delegators/%s/delegations", delegatorAddr), nil) @@ -1414,6 +1397,21 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. return signingInfo } +// ---------------------------------------------------------------------- +// ICS 23 - SlashingList +// ---------------------------------------------------------------------- +// GET /slashing/signing_infos Get sign info of all validators with pagination +func getSigningInfoList(t *testing.T, port string) []slashing.ValidatorSigningInfo { + res, body := Request(t, port, "GET", "/slashing/signing_infos?page=1&limit=1", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var signingInfo []slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(body), &signingInfo) + require.Nil(t, err) + + return signingInfo +} + // TODO: Test this functionality, it is not currently in any of the tests // POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator func doUnjail( diff --git a/client/rest/rest.go b/client/rest/rest.go index 81d2cc57b..709ef0d4a 100644 --- a/client/rest/rest.go +++ b/client/rest/rest.go @@ -1,6 +1,7 @@ package rest import ( + "log" "net/http" "github.com/cosmos/cosmos-sdk/client" @@ -67,6 +68,8 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, } w.Header().Set("Content-Type", "application/json") - w.Write(output) + if _, err := w.Write(output); err != nil { + log.Printf("could not write response: %v", err) + } return } diff --git a/client/rpc/block.go b/client/rpc/block.go index 127e9c9c8..6d6e0a9fd 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -115,20 +115,19 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { vars := mux.Vars(r) height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.")) + rest.WriteErrorResponse(w, http.StatusBadRequest, + "ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.") return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) + rest.WriteErrorResponse(w, http.StatusNotFound, + "ERROR: Requested block height is bigger then the chain length.") return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) @@ -140,14 +139,12 @@ func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) diff --git a/client/rpc/root.go b/client/rpc/root.go index 45533e3ea..fa82b493f 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -2,6 +2,7 @@ package rpc import ( "fmt" + "log" "net/http" "github.com/gorilla/mux" @@ -26,7 +27,9 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { // cli version REST handler endpoint func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))) + if _, err := w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))); err != nil { + log.Printf("could not write response: %v", err) + } } // connected node version REST handler endpoint @@ -39,6 +42,8 @@ func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { } w.Header().Set("Content-Type", "application/json") - w.Write(version) + if _, err := w.Write(version); err != nil { + log.Printf("could not write response: %v", err) + } } } diff --git a/client/rpc/status.go b/client/rpc/status.go index e017fa860..e96889a78 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -2,6 +2,7 @@ package rpc import ( "fmt" + "log" "net/http" "strconv" @@ -71,8 +72,7 @@ func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -86,18 +86,18 @@ func NodeSyncingRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } syncing := status.SyncInfo.CatchingUp if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - w.Write([]byte(strconv.FormatBool(syncing))) + if _, err := w.Write([]byte(strconv.FormatBool(syncing))); err != nil { + log.Printf("could not write response: %v", err) + } } } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 90389bf64..0c81f8151 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -45,7 +45,7 @@ func ValidatorCommand(cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) - result, err := getValidators(cliCtx, height) + result, err := GetValidators(cliCtx, height) if err != nil { return err } @@ -113,7 +113,7 @@ func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error }, nil } -func getValidators(cliCtx context.CLIContext, height *int64) (ResultValidatorsOutput, error) { +func GetValidators(cliCtx context.CLIContext, height *int64) (ResultValidatorsOutput, error) { // get the node node, err := cliCtx.GetNode() if err != nil { @@ -160,22 +160,19 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.")) + rest.WriteErrorResponse(w, http.StatusBadRequest, "ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.") return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) + rest.WriteErrorResponse(w, http.StatusNotFound, "ERROR: Requested block height is bigger then the chain length.") return } - output, err := getValidators(cliCtx, &height) + output, err := GetValidators(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) @@ -187,15 +184,13 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - output, err := getValidators(cliCtx, &height) + output, err := GetValidators(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) diff --git a/client/tx/broadcast.go b/client/tx/broadcast.go index a0437b1cd..15afff00f 100644 --- a/client/tx/broadcast.go +++ b/client/tx/broadcast.go @@ -18,19 +18,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -const ( - // Returns with the response from CheckTx. - flagSync = "sync" - // Returns right away, with no response - flagAsync = "async" - // Only returns error if mempool.BroadcastTx errs (ie. problem with the app) or if we timeout waiting for tx to commit. - flagBlock = "block" -) - // BroadcastReq defines a tx broadcasting request. type BroadcastReq struct { - Tx auth.StdTx `json:"tx"` - Return string `json:"return"` + Tx auth.StdTx `json:"tx"` + Mode string `json:"mode"` } // BroadcastTxRequest implements a tx broadcasting handler that is responsible @@ -58,23 +49,9 @@ func BroadcastTxRequest(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle return } - var res interface{} - switch req.Return { - case flagBlock: - res, err = cliCtx.BroadcastTx(txBytes) - - case flagSync: - res, err = cliCtx.BroadcastTxSync(txBytes) - - case flagAsync: - res, err = cliCtx.BroadcastTxAsync(txBytes) - - default: - rest.WriteErrorResponse(w, http.StatusInternalServerError, - "unsupported return type. supported types: block, sync, async") - return - } + cliCtx = cliCtx.WithBroadcastMode(req.Mode) + res, err := cliCtx.BroadcastTx(txBytes) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -110,7 +87,7 @@ $ gaiacli tx broadcast ./mytxn.json } res, err := cliCtx.BroadcastTx(txBytes) - cliCtx.PrintOutput(res) + cliCtx.PrintOutput(res) // nolint:errcheck return err }, } diff --git a/client/tx/encode.go b/client/tx/encode.go index 75670aade..5ea64ea72 100644 --- a/client/tx/encode.go +++ b/client/tx/encode.go @@ -97,7 +97,7 @@ If you supply a dash (-) argument in place of an input filename, the command rea txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes) response := txEncodeRespStr(txBytesBase64) - cliCtx.PrintOutput(response) + cliCtx.PrintOutput(response) // nolint:errcheck return nil }, diff --git a/client/tx/query.go b/client/tx/query.go index 74bb38495..8bb8e10cd 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -1,14 +1,13 @@ package tx import ( - "encoding/hex" "fmt" "net/http" "strings" "github.com/gorilla/mux" "github.com/spf13/cobra" - ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/tendermint/tendermint/types" "github.com/spf13/viper" @@ -17,14 +16,100 @@ 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/auth" ) +const ( + flagTags = "tags" + flagPage = "page" + flagLimit = "limit" +) + +// ---------------------------------------------------------------------------- +// CLI +// ---------------------------------------------------------------------------- + +// SearchTxCmd returns a command to search through tagged transactions. +func SearchTxCmd(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "txs", + Short: "Search for paginated transactions that match a set of tags", + Long: strings.TrimSpace(` +Search for transactions that match the exact given tags where results are paginated. + +Example: +$ gaiacli query txs --tags ':&:' --page 1 --limit 30 +`), + RunE: func(cmd *cobra.Command, args []string) error { + tagsStr := viper.GetString(flagTags) + tagsStr = strings.Trim(tagsStr, "'") + + var tags []string + if strings.Contains(tagsStr, "&") { + tags = strings.Split(tagsStr, "&") + } else { + tags = append(tags, tagsStr) + } + + var tmTags []string + for _, tag := range tags { + if !strings.Contains(tag, ":") { + return fmt.Errorf("%s should be of the format :", tagsStr) + } else if strings.Count(tag, ":") > 1 { + return fmt.Errorf("%s should only contain one : pair", tagsStr) + } + + keyValue := strings.Split(tag, ":") + if keyValue[0] == types.TxHeightKey { + tag = fmt.Sprintf("%s=%s", keyValue[0], keyValue[1]) + } else { + tag = fmt.Sprintf("%s='%s'", keyValue[0], keyValue[1]) + } + tmTags = append(tmTags, tag) + } + + page := viper.GetInt(flagPage) + limit := viper.GetInt(flagLimit) + + cliCtx := context.NewCLIContext().WithCodec(cdc) + txs, err := SearchTxs(cliCtx, cdc, tmTags, page, limit) + if err != nil { + return err + } + + var output []byte + if cliCtx.Indent { + output, err = cdc.MarshalJSONIndent(txs, "", " ") + } else { + output, err = cdc.MarshalJSON(txs) + } + + if err != nil { + return err + } + + fmt.Println(string(output)) + return nil + }, + } + + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) + + cmd.Flags().String(flagTags, "", "tag:value list of tags that must match") + cmd.Flags().Uint32(flagPage, rest.DefaultPage, "Query a specific page of paginated results") + cmd.Flags().Uint32(flagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") + cmd.MarkFlagRequired(flagTags) + + return cmd +} + // QueryTxCmd implements the default command for a tx query. func QueryTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", - Short: "Matches this txhash over all committed blocks", + Short: "Find 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) @@ -46,76 +131,55 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) + return cmd } -func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) (out sdk.TxResponse, err error) { - hash, err := hex.DecodeString(hashHexStr) - if err != nil { - return out, err - } - - node, err := cliCtx.GetNode() - if err != nil { - return out, err - } - - res, err := node.Tx(hash, !cliCtx.TrustNode) - if err != nil { - return out, err - } - - if !cliCtx.TrustNode { - if err = ValidateTxResult(cliCtx, res); err != nil { - return out, err - } - } - - if out, err = formatTxResult(cdc, res); err != nil { - return out, err - } - - return out, nil -} - -// ValidateTxResult performs transaction verification -func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error { - if !cliCtx.TrustNode { - check, err := cliCtx.Verify(res.Height) - if err != nil { - return err - } - err = res.Proof.Validate(check.Header.DataHash) - if err != nil { - return err - } - } - return nil -} - -func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (sdk.TxResponse, error) { - tx, err := parseTx(cdc, res.Tx) - if err != nil { - return sdk.TxResponse{}, err - } - - return sdk.NewResponseResultTx(res, tx), nil -} - -func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { - var tx auth.StdTx - - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) - if err != nil { - return nil, err - } - - return tx, nil -} - +// ---------------------------------------------------------------------------- // REST +// ---------------------------------------------------------------------------- -// QueryTxRequestHandlerFn transaction query REST handler +// QueryTxsByTagsRequestHandlerFn implements a REST handler that searches for +// transactions by tags. +func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + tags []string + txs []sdk.TxResponse + page, limit int + ) + + err := r.ParseForm() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, + sdk.AppendMsgToErr("could not parse query parameters", err.Error())) + return + } + + if len(r.Form) == 0 { + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + return + } + + tags, page, limit, err = rest.ParseHTTPArgs(r) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txs, err = SearchTxs(cliCtx, cdc, tags, page, limit) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + } +} + +// QueryTxRequestHandlerFn implements a REST handler that queries a transaction +// by hash in a committed block. func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/client/tx/root.go b/client/tx/root.go index 104e0a57c..cb2c64609 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -10,7 +10,7 @@ import ( // register REST routes func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET") + r.HandleFunc("/txs", QueryTxsByTagsRequestHandlerFn(cliCtx, cdc)).Methods("GET") r.HandleFunc("/txs", BroadcastTxRequest(cliCtx, cdc)).Methods("POST") r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cdc, cliCtx)).Methods("POST") } diff --git a/client/tx/search.go b/client/tx/search.go deleted file mode 100644 index 718ab5d03..000000000 --- a/client/tx/search.go +++ /dev/null @@ -1,250 +0,0 @@ -package tx - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/cosmos/cosmos-sdk/client" - "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/rest" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/types" -) - -const ( - flagTags = "tags" - flagAny = "any" - flagPage = "page" - flagLimit = "limit" - defaultPage = 1 - defaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 -) - -// default client command to search through tagged transactions -func SearchTxCmd(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "txs", - Short: "Search for all transactions that match the given tags.", - Long: strings.TrimSpace(` -Search for transactions that match exactly the given tags. For example: - -$ gaiacli query txs --tags ':&:' --page 1 --limit 30 -`), - RunE: func(cmd *cobra.Command, args []string) error { - tagsStr := viper.GetString(flagTags) - tagsStr = strings.Trim(tagsStr, "'") - var tags []string - if strings.Contains(tagsStr, "&") { - tags = strings.Split(tagsStr, "&") - } else { - tags = append(tags, tagsStr) - } - - var tmTags []string - for _, tag := range tags { - if !strings.Contains(tag, ":") { - return fmt.Errorf("%s should be of the format :", tagsStr) - } else if strings.Count(tag, ":") > 1 { - return fmt.Errorf("%s should only contain one : pair", tagsStr) - } - - keyValue := strings.Split(tag, ":") - if keyValue[0] == types.TxHeightKey { - tag = fmt.Sprintf("%s=%s", keyValue[0], keyValue[1]) - } else { - tag = fmt.Sprintf("%s='%s'", keyValue[0], keyValue[1]) - } - tmTags = append(tmTags, tag) - } - page := viper.GetInt(flagPage) - limit := viper.GetInt(flagLimit) - - cliCtx := context.NewCLIContext().WithCodec(cdc) - txs, err := SearchTxs(cliCtx, cdc, tmTags, page, limit) - if err != nil { - return err - } - - var output []byte - if cliCtx.Indent { - output, err = cdc.MarshalJSONIndent(txs, "", " ") - } else { - output, err = cdc.MarshalJSON(txs) - } - - if err != nil { - return err - } - - fmt.Println(string(output)) - return nil - }, - } - - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) - cmd.Flags().String(flagTags, "", "tag:value list of tags that must match") - cmd.Flags().Int32(flagPage, defaultPage, "Query a specific page of paginated results") - cmd.Flags().Int32(flagLimit, defaultLimit, "Query number of transactions results per page returned") - cmd.MarkFlagRequired(flagTags) - return cmd -} - -// SearchTxs performs a search for transactions for a given set of tags via -// Tendermint RPC. It returns a slice of Info object containing txs and metadata. -// An error is returned if the query fails. -func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, error) { - if len(tags) == 0 { - return nil, errors.New("must declare at least one tag 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(tags, " AND ") - - // get the node - node, err := cliCtx.GetNode() - if err != nil { - return nil, err - } - - prove := !cliCtx.TrustNode - - res, err := node.TxSearch(query, prove, page, limit) - if err != nil { - return nil, err - } - - if prove { - for _, tx := range res.Txs { - err := ValidateTxResult(cliCtx, tx) - if err != nil { - return nil, err - } - } - } - - info, err := FormatTxResults(cdc, res.Txs) - if err != nil { - return nil, err - } - - return info, nil -} - -// parse the indexed txs into an array of Info -func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]sdk.TxResponse, error) { - var err error - out := make([]sdk.TxResponse, len(res)) - for i := range res { - out[i], err = formatTxResult(cdc, res[i]) - if err != nil { - return nil, err - } - } - return out, nil -} - -///////////////////////////////////////// -// REST - -// Search Tx REST Handler -func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var tags []string - var page, limit int - var txs []sdk.TxResponse - err := r.ParseForm() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, - sdk.AppendMsgToErr("could not parse query parameters", err.Error())) - return - } - if len(r.Form) == 0 { - rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) - return - } - - tags, page, limit, err = parseHTTPArgs(r) - - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txs, err = SearchTxs(cliCtx, cdc, tags, page, limit) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) - } -} - -func parseHTTPArgs(r *http.Request) (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 { - tag = fmt.Sprintf("%s=%s", key, value) - } else { - tag = fmt.Sprintf("%s='%s'", key, value) - } - tags = append(tags, tag) - } - - pageStr := r.FormValue("page") - if pageStr == "" { - page = defaultPage - } else { - page, err = strconv.Atoi(pageStr) - if err != nil { - return tags, page, limit, err - } else if page <= 0 { - return tags, page, limit, errors.New("page must greater than 0") - } - } - - limitStr := r.FormValue("limit") - if limitStr == "" { - limit = defaultLimit - } else { - limit, err = strconv.Atoi(limitStr) - if err != nil { - return tags, page, limit, err - } else if limit <= 0 { - return tags, page, limit, errors.New("limit must greater than 0") - } - } - - return tags, page, limit, nil -} diff --git a/client/tx/utils.go b/client/tx/utils.go new file mode 100644 index 000000000..41dfd72e8 --- /dev/null +++ b/client/tx/utils.go @@ -0,0 +1,173 @@ +package tx + +import ( + "encoding/hex" + "errors" + "strings" + "time" + + "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" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +// SearchTxs performs a search for transactions for a given set of tags via +// Tendermint RPC. It returns a slice of Info object containing txs and metadata. +// An error is returned if the query fails. +func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, error) { + if len(tags) == 0 { + return nil, errors.New("must declare at least one tag 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(tags, " 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(cdc, resTxs.Txs, resBlocks) + if err != nil { + return nil, err + } + + return txs, 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 auth.StdTx + + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) + if err != nil { + return nil, err + } + + return tx, nil +} + +func queryTx(cdc *codec.Codec, 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(cdc, resTx, resBlocks[resTx.Height]) + if err != nil { + return out, err + } + + return out, nil +} diff --git a/client/utils/utils.go b/client/utils/utils.go index 8d1c0b494..25d81c451 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -6,6 +6,8 @@ import ( "io/ioutil" "os" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -69,7 +71,17 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return err } - fmt.Fprintf(os.Stderr, "%s\n\n", cliCtx.Codec.MustMarshalJSON(stdSignMsg)) + var json []byte + if viper.GetBool(client.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 := client.BufferStdin() ok, err := client.GetConfirmation("confirm transaction before signing and broadcasting", buf) @@ -92,8 +104,11 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte // broadcast to a Tendermint node res, err := cliCtx.BroadcastTx(txBytes) - cliCtx.PrintOutput(res) - return err + if err != nil { + return err + } + + return cliCtx.PrintOutput(res) } // EnrichWithGas calculates the gas estimate that would be consumed by the @@ -108,7 +123,9 @@ func EnrichWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []s // 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, common.HexBytes) ([]byte, error), cdc *amino.Codec, txBytes []byte, adjustment float64) (estimate, adjusted uint64, err error) { +func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), + cdc *amino.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) @@ -149,15 +166,17 @@ func PrintUnsignedStdTx( return } -// SignStdTx appends a signature to a StdTx and returns a copy of a it. If appendSig +// 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 authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool, offline bool) (auth.StdTx, error) { +func SignStdTx( + txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, + stdTx auth.StdTx, appendSig bool, offline bool, +) (auth.StdTx, error) { + var signedStdTx auth.StdTx - keybase := txBldr.Keybase() - - info, err := keybase.Get(name) + info, err := txBldr.Keybase().Get(name) if err != nil { return signedStdTx, err } @@ -170,8 +189,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, } if !offline { - txBldr, err = populateAccountFromState( - txBldr, cliCtx, sdk.AccAddress(addr)) + txBldr, err = populateAccountFromState(txBldr, cliCtx, sdk.AccAddress(addr)) if err != nil { return signedStdTx, err } @@ -229,25 +247,21 @@ func ReadStdTxFromFile(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err return } -func populateAccountFromState(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, - addr sdk.AccAddress) (authtxb.TxBuilder, error) { - if txBldr.AccountNumber() == 0 { - accNum, err := cliCtx.GetAccountNumber(addr) - if err != nil { - return txBldr, err - } - txBldr = txBldr.WithAccountNumber(accNum) +func populateAccountFromState( + txBldr authtxb.TxBuilder, cliCtx context.CLIContext, addr sdk.AccAddress, +) (authtxb.TxBuilder, error) { + + accNum, err := cliCtx.GetAccountNumber(addr) + if err != nil { + return txBldr, err } - if txBldr.Sequence() == 0 { - accSeq, err := cliCtx.GetAccountSequence(addr) - if err != nil { - return txBldr, err - } - txBldr = txBldr.WithSequence(accSeq) + accSeq, err := cliCtx.GetAccountSequence(addr) + if err != nil { + return txBldr, err } - return txBldr, nil + return txBldr.WithAccountNumber(accNum).WithSequence(accSeq), nil } // GetTxEncoder return tx encoder from global sdk configuration if ones is defined. diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 36044513f..262115649 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -11,14 +11,12 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - // TODO: Remove once transfers are enabled. - gaiabank "github.com/cosmos/cosmos-sdk/cmd/gaia/app/x/bank" - 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" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -44,6 +42,8 @@ type GaiaApp struct { *bam.BaseApp cdc *codec.Codec + assertInvariantsBlockly bool + // keys to access the substores keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey @@ -67,11 +67,14 @@ type GaiaApp struct { mintKeeper mint.Keeper distrKeeper distr.Keeper govKeeper gov.Keeper + crisisKeeper crisis.Keeper paramsKeeper params.Keeper } // NewGaiaApp returns a reference to an initialized GaiaApp. -func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp { +func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest, assertInvariantsBlockly bool, + baseAppOptions ...func(*bam.BaseApp)) *GaiaApp { + cdc := MakeCodec() bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) @@ -143,6 +146,12 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, ) + app.crisisKeeper = crisis.NewKeeper( + app.paramsKeeper.Subspace(crisis.DefaultParamspace), + app.distrKeeper, + app.bankKeeper, + app.feeCollectionKeeper, + ) // register the staking hooks // NOTE: The stakingKeeper above is passed by reference, so that it can be @@ -151,22 +160,27 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b NewStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()), ) + // register the crisis routes + bank.RegisterInvariants(&app.crisisKeeper, app.accountKeeper) + distr.RegisterInvariants(&app.crisisKeeper, app.distrKeeper, app.stakingKeeper) + staking.RegisterInvariants(&app.crisisKeeper, app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper) + // register message routes - // - // TODO: Use standard bank router once transfers are enabled. app.Router(). - AddRoute(bank.RouterKey, gaiabank.NewHandler(app.bankKeeper)). + 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)) + AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper)). + AddRoute(crisis.RouterKey, crisis.NewHandler(app.crisisKeeper)) app.QueryRouter(). AddRoute(auth.QuerierRoute, auth.NewQuerier(app.accountKeeper)). AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)). AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)). AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)). - AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)) + AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)). + AddRoute(mint.QuerierRoute, mint.NewQuerier(app.mintKeeper)) // initialize BaseApp app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keyMint, app.keyDistr, @@ -197,6 +211,7 @@ func MakeCodec() *codec.Codec { slashing.RegisterCodec(cdc) gov.RegisterCodec(cdc) auth.RegisterCodec(cdc) + crisis.RegisterCodec(cdc) sdk.RegisterCodec(cdc) codec.RegisterCrypto(cdc) return cdc @@ -229,7 +244,9 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R validatorUpdates, endBlockerTags := staking.EndBlocker(ctx, app.stakingKeeper) tags = append(tags, endBlockerTags...) - app.assertRuntimeInvariants() + if app.assertInvariantsBlockly { + app.assertRuntimeInvariants() + } return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, @@ -262,6 +279,7 @@ func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisSt bank.InitGenesis(ctx, app.bankKeeper, genesisState.BankData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData.Validators.ToSDKValidators()) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) + crisis.InitGenesis(ctx, app.crisisKeeper, genesisState.CrisisData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) // validate genesis state diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 0ee1596ea..ec38f06a6 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/crisis" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/db" @@ -35,6 +36,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { mint.DefaultGenesisState(), distr.DefaultGenesisState(), gov.DefaultGenesisState(), + crisis.DefaultGenesisState(), slashing.DefaultGenesisState(), ) @@ -53,11 +55,11 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { func TestGaiadExport(t *testing.T) { db := db.NewMemDB() - gapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true) + gapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, false) setGenesis(gapp) // Making a new app object with the db, so that initchain hasn't been called - newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true) + newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, false) _, _, err := newGapp.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } diff --git a/cmd/gaia/app/export.go b/cmd/gaia/app/export.go index 773a71838..5174c1498 100644 --- a/cmd/gaia/app/export.go +++ b/cmd/gaia/app/export.go @@ -11,6 +11,7 @@ import ( 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/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -46,6 +47,7 @@ func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLis mint.ExportGenesis(ctx, app.mintKeeper), distr.ExportGenesis(ctx, app.distrKeeper), gov.ExportGenesis(ctx, app.govKeeper), + crisis.ExportGenesis(ctx, app.crisisKeeper), slashing.ExportGenesis(ctx, app.slashingKeeper), ) appState, err = codec.MarshalJSONIndent(app.cdc, genState) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 65a64033b..997cd131c 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -17,6 +17,7 @@ import ( 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/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -39,6 +40,7 @@ type GenesisState struct { MintData mint.GenesisState `json:"mint"` DistrData distr.GenesisState `json:"distr"` GovData gov.GenesisState `json:"gov"` + CrisisData crisis.GenesisState `json:"crisis"` SlashingData slashing.GenesisState `json:"slashing"` GenTxs []json.RawMessage `json:"gentxs"` } @@ -46,7 +48,7 @@ type GenesisState struct { func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, bankData bank.GenesisState, stakingData staking.GenesisState, mintData mint.GenesisState, - distrData distr.GenesisState, govData gov.GenesisState, + distrData distr.GenesisState, govData gov.GenesisState, crisisData crisis.GenesisState, slashingData slashing.GenesisState) GenesisState { return GenesisState{ @@ -57,6 +59,7 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, MintData: mintData, DistrData: distrData, GovData: govData, + CrisisData: crisisData, SlashingData: slashingData, } } @@ -209,6 +212,7 @@ func NewDefaultGenesisState() GenesisState { MintData: mint.DefaultGenesisState(), DistrData: distr.DefaultGenesisState(), GovData: gov.DefaultGenesisState(), + CrisisData: crisis.DefaultGenesisState(), SlashingData: slashing.DefaultGenesisState(), GenTxs: nil, } @@ -246,6 +250,9 @@ func GaiaValidateGenesisState(genesisState GenesisState) error { if err := gov.ValidateGenesis(genesisState.GovData); err != nil { return err } + if err := crisis.ValidateGenesis(genesisState.CrisisData); err != nil { + return err + } return slashing.ValidateGenesis(genesisState.SlashingData) } diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 4d39b4e48..300f4afcd 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -41,7 +41,7 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { msg := msgs[0].(staking.MsgCreateValidator) acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddress)) - acc.Coins = sdk.Coins{sdk.NewInt64Coin(defaultBondDenom, 150)} + acc.Coins = sdk.NewCoins(sdk.NewInt64Coin(defaultBondDenom, 150)) genAccs[i] = NewGenesisAccount(&acc) stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.Add(sdk.NewInt(150)) // increase the supply } @@ -55,7 +55,7 @@ func TestToAccount(t *testing.T) { priv := ed25519.GenPrivKey() addr := sdk.AccAddress(priv.PubKey().Address()) authAcc := auth.NewBaseAccountWithAddress(addr) - authAcc.SetCoins(sdk.Coins{sdk.NewInt64Coin(defaultBondDenom, 150)}) + authAcc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(defaultBondDenom, 150))) genAcc := NewGenesisAccount(&authAcc) acc := genAcc.ToAccount() require.IsType(t, &auth.BaseAccount{}, acc) diff --git a/cmd/gaia/app/invariants.go b/cmd/gaia/app/invariants.go index 535cbff37..841732ca1 100644 --- a/cmd/gaia/app/invariants.go +++ b/cmd/gaia/app/invariants.go @@ -7,20 +7,8 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" - distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" - stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation" ) -func (app *GaiaApp) runtimeInvariants() []sdk.Invariant { - return []sdk.Invariant{ - banksim.NonnegativeBalanceInvariant(app.accountKeeper), - distrsim.NonNegativeOutstandingInvariant(app.distrKeeper), - stakingsim.SupplyInvariants(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper), - stakingsim.NonNegativePowerInvariant(app.stakingKeeper), - } -} - func (app *GaiaApp) assertRuntimeInvariants() { ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1}) app.assertRuntimeInvariantsOnContext(ctx) @@ -28,10 +16,12 @@ func (app *GaiaApp) assertRuntimeInvariants() { func (app *GaiaApp) assertRuntimeInvariantsOnContext(ctx sdk.Context) { start := time.Now() - invariants := app.runtimeInvariants() - for _, inv := range invariants { - if err := inv(ctx); err != nil { - panic(fmt.Errorf("invariant broken: %s", err)) + invarRoutes := app.crisisKeeper.Routes() + for _, ir := range invarRoutes { + if err := ir.Invar(ctx); err != nil { + panic(fmt.Errorf("invariant broken: %s\n"+ + "\tCRITICAL please submit the following transaction:\n"+ + "\t\t gaiacli tx crisis invariant-broken %v %v", err, ir.ModuleName, ir.Route)) } } end := time.Now() diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 2cba35458..247cc262a 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -29,7 +29,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/mint" - "github.com/cosmos/cosmos-sdk/x/mock/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/slashing" slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" "github.com/cosmos/cosmos-sdk/x/staking" @@ -43,19 +43,30 @@ var ( blockSize int enabled bool verbose bool + lean bool commit bool period int ) func init() { - flag.StringVar(&genesisFile, "SimulationGenesis", "", "Custom simulation genesis file") - flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed") - flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "Number of blocks") - flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block") - flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation") - flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output") - flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit") - flag.IntVar(&period, "SimulationPeriod", 1, "Run slow invariants only once every period assertions") + flag.StringVar(&genesisFile, "SimulationGenesis", "", "custom simulation genesis file") + flag.Int64Var(&seed, "SimulationSeed", 42, "simulation random seed") + flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "number of blocks") + flag.IntVar(&blockSize, "SimulationBlockSize", 200, "operations per block") + flag.BoolVar(&enabled, "SimulationEnabled", false, "enable the simulation") + flag.BoolVar(&verbose, "SimulationVerbose", false, "verbose log output") + flag.BoolVar(&lean, "SimulationLean", false, "lean simulation log output") + flag.BoolVar(&commit, "SimulationCommit", false, "have the simulation commit") + flag.IntVar(&period, "SimulationPeriod", 1, "run slow invariants only once every period assertions") +} + +// helper function for populating input for SimulateFromSeed +func getSimulateFromSeedInput(tb testing.TB, app *GaiaApp) ( + testing.TB, *baseapp.BaseApp, simulation.AppStateFn, int64, + simulation.WeightedOperations, sdk.Invariants, int, int, bool, bool) { + + return tb, app.BaseApp, appStateFn, seed, + testAndRunTxs(app), invariants(app), numBlocks, blockSize, commit, lean } func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { @@ -92,7 +103,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest numInitiallyBonded = numAccs } fmt.Printf("Selected randomly generated parameters for simulated genesis:\n"+ - "\t{amount of steak per account: %v, initially bonded validators: %v}\n", + "\t{amount of stake per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded) // randomly generate some genesis accounts @@ -265,8 +276,8 @@ func randIntBetween(r *rand.Rand, min, max int) int { func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ {5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)}, - {100, banksim.SendMsg(app.accountKeeper, app.bankKeeper)}, - {10, banksim.SingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper)}, + {100, banksim.SimulateMsgSend(app.accountKeeper, app.bankKeeper)}, + {10, banksim.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper)}, {50, distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)}, @@ -283,12 +294,10 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { func invariants(app *GaiaApp) []sdk.Invariant { return []sdk.Invariant{ - simulation.PeriodicInvariant(banksim.NonnegativeBalanceInvariant(app.accountKeeper), period, 0), - simulation.PeriodicInvariant(govsim.AllInvariants(), period, 0), - simulation.PeriodicInvariant(distrsim.AllInvariants(app.distrKeeper, app.stakingKeeper), period, 0), - simulation.PeriodicInvariant(stakingsim.AllInvariants(app.stakingKeeper, app.feeCollectionKeeper, + simulation.PeriodicInvariant(bank.NonnegativeBalanceInvariant(app.accountKeeper), period, 0), + simulation.PeriodicInvariant(distr.AllInvariants(app.distrKeeper, app.stakingKeeper), period, 0), + simulation.PeriodicInvariant(staking.AllInvariants(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper), period, 0), - simulation.PeriodicInvariant(slashingsim.AllInvariants(), period, 0), } } @@ -310,18 +319,11 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { db.Close() os.RemoveAll(dir) }() - app := NewGaiaApp(logger, db, nil, true) + app := NewGaiaApp(logger, db, nil, true, false) // Run randomized simulation // TODO parameterize numbers, save for a later PR - _, err := simulation.SimulateFromSeed( - b, app.BaseApp, appStateFn, seed, - testAndRunTxs(app), - invariants(app), // these shouldn't get ran - numBlocks, - blockSize, - commit, - ) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(b, app)) if err != nil { fmt.Println(err) b.Fail() @@ -352,18 +354,11 @@ func TestFullGaiaSimulation(t *testing.T) { db.Close() os.RemoveAll(dir) }() - app := NewGaiaApp(logger, db, nil, true, fauxMerkleModeOpt) + app := NewGaiaApp(logger, db, nil, true, false, fauxMerkleModeOpt) require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - _, err := simulation.SimulateFromSeed( - t, app.BaseApp, appStateFn, seed, - testAndRunTxs(app), - invariants(app), - numBlocks, - blockSize, - commit, - ) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) if commit { // for memdb: // fmt.Println("Database Size", db.Stats()["database.size"]) @@ -393,18 +388,12 @@ func TestGaiaImportExport(t *testing.T) { db.Close() os.RemoveAll(dir) }() - app := NewGaiaApp(logger, db, nil, true, fauxMerkleModeOpt) + app := NewGaiaApp(logger, db, nil, true, false, fauxMerkleModeOpt) require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - _, err := simulation.SimulateFromSeed( - t, app.BaseApp, appStateFn, seed, - testAndRunTxs(app), - invariants(app), - numBlocks, - blockSize, - commit, - ) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) + if commit { // for memdb: // fmt.Println("Database Size", db.Stats()["database.size"]) @@ -426,7 +415,7 @@ func TestGaiaImportExport(t *testing.T) { newDB.Close() os.RemoveAll(newDir) }() - newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, fauxMerkleModeOpt) + newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, false, fauxMerkleModeOpt) require.Equal(t, "GaiaApp", newApp.Name()) var genesisState GenesisState err = app.cdc.UnmarshalJSON(appState, &genesisState) @@ -446,7 +435,8 @@ func TestGaiaImportExport(t *testing.T) { storeKeysPrefixes := []StoreKeysPrefixes{ {app.keyMain, newApp.keyMain, [][]byte{}}, {app.keyAccount, newApp.keyAccount, [][]byte{}}, - {app.keyStaking, newApp.keyStaking, [][]byte{staking.UnbondingQueueKey, staking.RedelegationQueueKey, staking.ValidatorQueueKey}}, // ordering may change but it doesn't matter + {app.keyStaking, newApp.keyStaking, [][]byte{staking.UnbondingQueueKey, + staking.RedelegationQueueKey, staking.ValidatorQueueKey}}, // ordering may change but it doesn't matter {app.keySlashing, newApp.keySlashing, [][]byte{}}, {app.keyMint, newApp.keyMint, [][]byte{}}, {app.keyDistr, newApp.keyDistr, [][]byte{}}, @@ -488,18 +478,12 @@ func TestGaiaSimulationAfterImport(t *testing.T) { db.Close() os.RemoveAll(dir) }() - app := NewGaiaApp(logger, db, nil, true, fauxMerkleModeOpt) + app := NewGaiaApp(logger, db, nil, true, false, fauxMerkleModeOpt) require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - stopEarly, err := simulation.SimulateFromSeed( - t, app.BaseApp, appStateFn, seed, - testAndRunTxs(app), - invariants(app), - numBlocks, - blockSize, - commit, - ) + stopEarly, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) + if commit { // for memdb: // fmt.Println("Database Size", db.Stats()["database.size"]) @@ -530,21 +514,14 @@ func TestGaiaSimulationAfterImport(t *testing.T) { newDB.Close() os.RemoveAll(newDir) }() - newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, fauxMerkleModeOpt) + newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, false, fauxMerkleModeOpt) require.Equal(t, "GaiaApp", newApp.Name()) newApp.InitChain(abci.RequestInitChain{ AppStateBytes: appState, }) // Run randomized simulation on imported app - _, err = simulation.SimulateFromSeed( - t, newApp.BaseApp, appStateFn, seed, - testAndRunTxs(newApp), - invariants(newApp), - numBlocks, - blockSize, - commit, - ) + _, err = simulation.SimulateFromSeed(getSimulateFromSeedInput(t, newApp)) require.Nil(t, err) } @@ -565,7 +542,7 @@ func TestAppStateDeterminism(t *testing.T) { for j := 0; j < numTimesToRunPerSeed; j++ { logger := log.NewNopLogger() db := dbm.NewMemDB() - app := NewGaiaApp(logger, db, nil, true) + app := NewGaiaApp(logger, db, nil, true, false) // Run randomized simulation simulation.SimulateFromSeed( @@ -575,6 +552,7 @@ func TestAppStateDeterminism(t *testing.T) { 50, 100, true, + false, ) appHash := app.LastCommitID().Hash appHashList[j] = appHash diff --git a/cmd/gaia/app/x/bank/handler.go b/cmd/gaia/app/x/bank/handler.go deleted file mode 100644 index d3aa3415d..000000000 --- a/cmd/gaia/app/x/bank/handler.go +++ /dev/null @@ -1,100 +0,0 @@ -// Package bank contains a forked version of the bank module. It only contains -// a modified message handler to support a very limited form of transfers during -// mainnet launch -- MsgMultiSend messages. -// -// NOTE: This fork should be removed entirely once transfers are enabled and -// the Gaia router should be reset to using the original bank module handler. -package bank - -import ( - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank" -) - -var ( - uatomDenom = "uatom" - atomsToUatoms = int64(1000000) - - // BurnedCoinsAccAddr represents the burn account address used for - // MsgMultiSend message during the period for which transfers are disabled. - // Its Bech32 address is cosmos1x4p90uuy63fqzsheamn48vq88q3eusykf0a69v. - BurnedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("bankBurnedCoins"))) -) - -// NewHandler returns a handler for "bank" type messages. -func NewHandler(k bank.Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case bank.MsgSend: - return handleMsgSend(ctx, k, msg) - - case bank.MsgMultiSend: - return handleMsgMultiSend(ctx, k, msg) - - default: - errMsg := "Unrecognized bank Msg type: %s" + msg.Type() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// handleMsgSend implements a MsgSend message handler. It operates no differently -// than the standard bank module MsgSend message handler in that it transfers -// an amount from one account to another under the condition of transfers being -// enabled. -func handleMsgSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgSend) sdk.Result { - // No need to modify handleMsgSend as the forked module requires no changes, - // so we can just call the standard bank modules handleMsgSend since we know - // the message is of type MsgSend. - return bank.NewHandler(k)(ctx, msg) -} - -// handleMsgMultiSend implements a modified forked version of a MsgMultiSend -// message handler. If transfers are disabled, a modified version of MsgMultiSend -// is allowed where there must be a single input and only two outputs. The first -// of the two outputs must be to a specific burn address defined by -// burnedCoinsAccAddr. In addition, the output amounts must be of 9atom and -// 1uatom respectively. -func handleMsgMultiSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgMultiSend) sdk.Result { - // NOTE: totalIn == totalOut should already have been checked - if !k.GetSendEnabled(ctx) { - if !validateMultiSendTransfersDisabled(msg) { - return bank.ErrSendDisabled(k.Codespace()).Result() - } - } - - tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) - if err != nil { - return err.Result() - } - - return sdk.Result{ - Tags: tags, - } -} - -func validateMultiSendTransfersDisabled(msg bank.MsgMultiSend) bool { - nineAtoms := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 9*atomsToUatoms)} - oneAtom := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 1*atomsToUatoms)} - - if len(msg.Inputs) != 1 { - return false - } - if len(msg.Outputs) != 2 { - return false - } - - if !msg.Outputs[0].Address.Equals(BurnedCoinsAccAddr) { - return false - } - if !msg.Outputs[0].Coins.IsEqual(nineAtoms) { - return false - } - if !msg.Outputs[1].Coins.IsEqual(oneAtom) { - return false - } - - return true -} diff --git a/cmd/gaia/app/x/bank/handler_test.go b/cmd/gaia/app/x/bank/handler_test.go deleted file mode 100644 index c799e03f8..000000000 --- a/cmd/gaia/app/x/bank/handler_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package bank - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "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" -) - -var ( - addrs = []sdk.AccAddress{ - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - } - - initAmt = sdk.NewInt(atomsToUatoms * 100) -) - -type testInput struct { - ctx sdk.Context - accKeeper auth.AccountKeeper - bankKeeper bank.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - bank.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - for _, addr := range addrs { - _, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)}) - require.NoError(t, err) - } - - return testInput{ctx, accKeeper, bankKeeper} -} - -func TestHandlerMsgSendTransfersDisabled(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, false) - - handler := NewHandler(input.bankKeeper) - amt := sdk.NewInt(atomsToUatoms * 5) - msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)}) - - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)}) -} - -func TestHandlerMsgSendTransfersEnabled(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, true) - - handler := NewHandler(input.bankKeeper) - amt := sdk.NewInt(atomsToUatoms * 5) - msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)}) - - res := handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - balance := initAmt.Sub(amt) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - balance = initAmt.Add(amt) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) -} - -func TestHandlerMsgMultiSendTransfersDisabledBurn(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, false) - - handler := NewHandler(input.bankKeeper) - totalAmt := sdk.NewInt(10 * atomsToUatoms) - burnAmt := sdk.NewInt(9 * atomsToUatoms) - transAmt := sdk.NewInt(1 * atomsToUatoms) - msg := bank.NewMsgMultiSend( - []bank.Input{ - bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}), - }, - []bank.Output{ - bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}), - bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}), - }, - ) - - res := handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - balance := initAmt.Sub(totalAmt) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - balance = initAmt.Add(transAmt) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) - - burn := input.accKeeper.GetAccount(input.ctx, BurnedCoinsAccAddr) - require.Equal(t, burn.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}) -} - -func TestHandlerMsgMultiSendTransfersDisabledInvalidBurn(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, false) - - handler := NewHandler(input.bankKeeper) - totalAmt := sdk.NewInt(15 * atomsToUatoms) - burnAmt := sdk.NewInt(10 * atomsToUatoms) - transAmt := sdk.NewInt(5 * atomsToUatoms) - msg := bank.NewMsgMultiSend( - []bank.Input{ - bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}), - }, - []bank.Output{ - bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}), - bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}), - }, - ) - - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)}) -} - -func TestHandlerMsgMultiSendTransfersEnabled(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, true) - - handler := NewHandler(input.bankKeeper) - totalAmt := sdk.NewInt(10 * atomsToUatoms) - outAmt := sdk.NewInt(5 * atomsToUatoms) - msg := bank.NewMsgMultiSend( - []bank.Input{ - bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}), - }, - []bank.Output{ - bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}), - bank.NewOutput(addrs[2], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}), - }, - ) - - res := handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - balance := initAmt.Sub(totalAmt) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) - - out1 := input.accKeeper.GetAccount(input.ctx, addrs[1]) - balance = initAmt.Add(outAmt) - require.Equal(t, out1.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) - - out2 := input.accKeeper.GetAccount(input.ctx, addrs[2]) - balance = initAmt.Add(outAmt) - require.Equal(t, out2.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)}) -} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 3b4784fe2..b71d6c1c2 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -1,3 +1,5 @@ +// +build cli_test + package clitest import ( @@ -361,7 +363,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) // Generate a create validator transaction and ensure correctness - success, stdout, stderr := f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewInt64Coin(denom, 2), "--generate-only") + success, stdout, stderr := f.TxStakingCreateValidator(barAddr.String(), consPubKey, sdk.NewInt64Coin(denom, 2), "--generate-only") require.True(f.T, success) require.Empty(f.T, stderr) @@ -394,13 +396,13 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.NotZero(t, validatorDelegations[0].Shares) // unbond a single share - unbondTokens := sdk.TokensFromTendermintPower(1) - success = f.TxStakingUnbond(keyBar, unbondTokens.String(), barVal, "-y") + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(1)) + success = f.TxStakingUnbond(keyBar, unbondAmt.String(), barVal, "-y") require.True(t, success) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure bonded staking is correct - remainingTokens := newValTokens.Sub(unbondTokens) + remainingTokens := newValTokens.Sub(unbondAmt.Amount) validator = f.QueryStakingValidator(barVal) require.Equal(t, remainingTokens, validator.Tokens) @@ -437,7 +439,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Test submit generate only for submit proposal proposalTokens := sdk.TokensFromTendermintPower(5) success, stdout, stderr := f.TxGovSubmitProposal( - keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only", "-y") + fooAddr.String(), "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only", "-y") require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) @@ -463,12 +465,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Ensure propsal is directly queryable proposal1 := f.QueryGovProposal(1) - require.Equal(t, uint64(1), proposal1.GetProposalID()) - require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) + require.Equal(t, uint64(1), proposal1.ProposalID) + require.Equal(t, gov.StatusDepositPeriod, proposal1.Status) // Ensure query proposals returns properly proposalsQuery = f.QueryGovProposals() - require.Equal(t, uint64(1), proposalsQuery[0].GetProposalID()) + require.Equal(t, uint64(1), proposalsQuery[0].ProposalID) // Query the deposits on the proposal deposit := f.QueryGovDeposit(1, fooAddr) @@ -476,7 +478,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Test deposit generate only depositTokens := sdk.TokensFromTendermintPower(10) - success, stdout, stderr = f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens), "--generate-only") + success, stdout, stderr = f.TxGovDeposit(1, fooAddr.String(), sdk.NewCoin(denom, depositTokens), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -507,11 +509,11 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Fetch the proposal and ensure it is now in the voting period proposal1 = f.QueryGovProposal(1) - require.Equal(t, uint64(1), proposal1.GetProposalID()) - require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus()) + require.Equal(t, uint64(1), proposal1.ProposalID) + require.Equal(t, gov.StatusVotingPeriod, proposal1.Status) // Test vote generate only - success, stdout, stderr = f.TxGovVote(1, gov.OptionYes, keyFoo, "--generate-only") + success, stdout, stderr = f.TxGovVote(1, gov.OptionYes, fooAddr.String(), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -544,7 +546,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Ensure the proposal returns as in the voting period proposalsQuery = f.QueryGovProposals("--status=VotingPeriod") - require.Equal(t, uint64(1), proposalsQuery[0].GetProposalID()) + require.Equal(t, uint64(1), proposalsQuery[0].ProposalID) // submit a second test proposal f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewCoin(denom, proposalTokens), "-y") @@ -552,7 +554,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Test limit on proposals query proposalsQuery = f.QueryGovProposals("--limit=1") - require.Equal(t, uint64(2), proposalsQuery[0].GetProposalID()) + require.Equal(t, uint64(2), proposalsQuery[0].ProposalID) f.Cleanup() } @@ -618,7 +620,7 @@ func TestGaiaCLIValidateSignatures(t *testing.T) { barAddr := f.KeyAddress(keyBar) // generate sendTx with default gas - success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + success, stdout, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") require.True(t, success) require.Empty(t, stderr) @@ -669,7 +671,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test generate sendTx with default gas sendTokens := sdk.TokensFromTendermintPower(10) - success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only") + success, stdout, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) @@ -678,7 +680,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test generate sendTx with --gas=$amount - success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=100", "--generate-only") + success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=100", "--generate-only") require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -687,7 +689,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test generate sendTx, estimate gas - success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "--generate-only") + success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "--generate-only") require.True(t, success) require.NotEmpty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -764,7 +766,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) { tests.WaitForNextNBlocksTM(1, f.Port) // Test generate sendTx with multisig - success, stdout, _ := f.TxSend(keyFooBarBaz, barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + success, stdout, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") require.True(t, success) // Write the output to disk @@ -808,8 +810,10 @@ func TestGaiaCLIEncode(t *testing.T) { // Build a testing transaction and write it to disk barAddr := f.KeyAddress(keyBar) + keyAddr := f.KeyAddress(keyFoo) + sendTokens := sdk.TokensFromTendermintPower(10) - success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only", "--memo", "deadbeef") + success, stdout, stderr := f.TxSend(keyAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only", "--memo", "deadbeef") require.True(t, success) require.Empty(t, stderr) @@ -853,7 +857,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) { require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) // Test generate sendTx with multisig - success, stdout, _ := f.TxSend(keyFooBarBaz, barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + success, stdout, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") require.True(t, success) // Write the output to disk @@ -915,7 +919,7 @@ func TestGaiaCLIMultisign(t *testing.T) { require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) // Test generate sendTx with multisig - success, stdout, stderr := f.TxSend(keyFooBarBaz, bazAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + success, stdout, stderr := f.TxSend(fooBarBazAddr.String(), bazAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") require.True(t, success) require.Empty(t, stderr) @@ -963,6 +967,7 @@ func TestGaiaCLIConfig(t *testing.T) { node := fmt.Sprintf("%s:%s", f.RPCAddr, f.Port) // Set available configuration options + f.CLIConfig("broadcast-mode", "block") f.CLIConfig("node", node) f.CLIConfig("output", "text") f.CLIConfig("trust-node", "true") @@ -972,7 +977,8 @@ func TestGaiaCLIConfig(t *testing.T) { config, err := ioutil.ReadFile(path.Join(f.GCLIHome, "config", "config.toml")) require.NoError(t, err) - expectedConfig := fmt.Sprintf(`chain-id = "%s" + expectedConfig := fmt.Sprintf(`broadcast-mode = "block" +chain-id = "%s" indent = true node = "%s" output = "text" diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 21419ad96..aac07abb2 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -104,15 +104,15 @@ func (f Fixtures) GenesisState() app.GenesisState { return appState } -// InitFixtures is called at the beginning of a test -// and initializes a chain with 1 validator +// InitFixtures is called at the beginning of a test and initializes a chain +// with 1 validator. func InitFixtures(t *testing.T) (f *Fixtures) { f = NewFixtures(t) - // Reset test state + // reset test state f.UnsafeResetAll() - // Ensure keystore has foo and bar keys + // ensure keystore has foo and bar keys f.KeysDelete(keyFoo) f.KeysDelete(keyBar) f.KeysDelete(keyBar) @@ -124,14 +124,16 @@ func InitFixtures(t *testing.T) (f *Fixtures) { f.KeysAdd(keyFooBarBaz, "--multisig-threshold=2", fmt.Sprintf( "--multisig=%s,%s,%s", keyFoo, keyBar, keyBaz)) - // Ensure that CLI output is in JSON format + // ensure that CLI output is in JSON format f.CLIConfig("output", "json") // NOTE: GDInit sets the ChainID f.GDInit(keyFoo) - f.CLIConfig("chain-id", f.ChainID) - // Start an account with tokens + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("broadcast-mode", "block") + + // start an account with tokens f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) f.AddGenesisAccount( f.KeyAddress(keyVesting), startCoins, @@ -139,8 +141,10 @@ func InitFixtures(t *testing.T) (f *Fixtures) { fmt.Sprintf("--vesting-start-time=%d", time.Now().UTC().UnixNano()), fmt.Sprintf("--vesting-end-time=%d", time.Now().Add(60*time.Second).UTC().UnixNano()), ) + f.GenTx(keyFoo) f.CollectGenTxs() + return } @@ -163,7 +167,7 @@ func (f *Fixtures) Flags() string { // UnsafeResetAll is gaiad unsafe-reset-all func (f *Fixtures) UnsafeResetAll(flags ...string) { - cmd := fmt.Sprintf("gaiad --home=%s unsafe-reset-all", f.GDHome) + cmd := fmt.Sprintf("../../../build/gaiad --home=%s unsafe-reset-all", f.GDHome) executeWrite(f.T, addFlags(cmd, flags)) err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx")) require.NoError(f.T, err) @@ -172,7 +176,7 @@ func (f *Fixtures) UnsafeResetAll(flags ...string) { // GDInit is gaiad init // NOTE: GDInit sets the ChainID for the Fixtures instance func (f *Fixtures) GDInit(moniker string, flags ...string) { - cmd := fmt.Sprintf("gaiad init -o --home=%s %s", f.GDHome, moniker) + cmd := fmt.Sprintf("../../../build/gaiad init -o --home=%s %s", f.GDHome, moniker) _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass) var chainID string @@ -189,25 +193,25 @@ func (f *Fixtures) GDInit(moniker string, flags ...string) { // AddGenesisAccount is gaiad add-genesis-account func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) { - cmd := fmt.Sprintf("gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome) + cmd := fmt.Sprintf("../../../build/gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } // GenTx is gaiad gentx func (f *Fixtures) GenTx(name string, flags ...string) { - cmd := fmt.Sprintf("gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome) + cmd := fmt.Sprintf("../../../build/gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // CollectGenTxs is gaiad collect-gentxs func (f *Fixtures) CollectGenTxs(flags ...string) { - cmd := fmt.Sprintf("gaiad collect-gentxs --home=%s", f.GDHome) + cmd := fmt.Sprintf("../../../build/gaiad collect-gentxs --home=%s", f.GDHome) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // GDStart runs gaiad start with the appropriate flags and returns a process func (f *Fixtures) GDStart(flags ...string) *tests.Process { - cmd := fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr) + cmd := fmt.Sprintf("../../../build/gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr) proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) tests.WaitForTMStart(f.Port) tests.WaitForNextNBlocksTM(1, f.Port) @@ -216,7 +220,7 @@ func (f *Fixtures) GDStart(flags ...string) *tests.Process { // GDTendermint returns the results of gaiad tendermint [query] func (f *Fixtures) GDTendermint(query string) string { - cmd := fmt.Sprintf("gaiad tendermint %s --home=%s", query, f.GDHome) + cmd := fmt.Sprintf("../../../build/gaiad tendermint %s --home=%s", query, f.GDHome) success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) require.Empty(f.T, stderr) require.True(f.T, success) @@ -225,7 +229,7 @@ func (f *Fixtures) GDTendermint(query string) string { // ValidateGenesis runs gaiad validate-genesis func (f *Fixtures) ValidateGenesis() { - cmd := fmt.Sprintf("gaiad validate-genesis --home=%s", f.GDHome) + cmd := fmt.Sprintf("../../../build/gaiad validate-genesis --home=%s", f.GDHome) executeWriteCheckErr(f.T, cmd) } @@ -234,31 +238,31 @@ func (f *Fixtures) ValidateGenesis() { // KeysDelete is gaiacli keys delete func (f *Fixtures) KeysDelete(name string, flags ...string) { - cmd := fmt.Sprintf("gaiacli keys delete --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("../../../build/gaiacli keys delete --home=%s %s", f.GCLIHome, name) executeWrite(f.T, addFlags(cmd, append(append(flags, "-y"), "-f"))) } // KeysAdd is gaiacli keys add func (f *Fixtures) KeysAdd(name string, flags ...string) { - cmd := fmt.Sprintf("gaiacli keys add --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s %s", f.GCLIHome, name) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // KeysAddRecover prepares gaiacli keys add --recover func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) { - cmd := fmt.Sprintf("gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) } // KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { - cmd := fmt.Sprintf("gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index) + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) } // KeysShow is gaiacli keys show func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { - cmd := fmt.Sprintf("gaiacli keys show --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("../../../build/gaiacli keys show --home=%s %s", f.GCLIHome, name) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ko keys.KeyOutput err := clientkeys.UnmarshalJSON([]byte(out), &ko) @@ -279,7 +283,7 @@ func (f *Fixtures) KeyAddress(name string) sdk.AccAddress { // CLIConfig is gaiacli config func (f *Fixtures) CLIConfig(key, value string, flags ...string) { - cmd := fmt.Sprintf("gaiacli config --home=%s %s %s", f.GCLIHome, key, value) + cmd := fmt.Sprintf("../../../build/gaiacli config --home=%s %s %s", f.GCLIHome, key, value) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } @@ -288,7 +292,7 @@ func (f *Fixtures) CLIConfig(key, value string, flags ...string) { // TxSend is gaiacli tx send func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -296,25 +300,25 @@ func (f *Fixtures) txSendWithConfirm( from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string, ) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, app.DefaultKeyPass) } // TxSign is gaiacli tx sign func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName) + cmd := fmt.Sprintf("../../../build/gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxBroadcast is gaiacli tx broadcast func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx broadcast %v %v", f.Flags(), fileName) + cmd := fmt.Sprintf("../../../build/gaiacli tx broadcast %v %v", f.Flags(), fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxEncode is gaiacli tx encode func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx encode %v %v", f.Flags(), fileName) + cmd := fmt.Sprintf("../../../build/gaiacli tx encode %v %v", f.Flags(), fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -322,7 +326,7 @@ func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, str func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx multisign %v %s %s %s", f.Flags(), + cmd := fmt.Sprintf("../../../build/gaiacli tx multisign %v %s %s %s", f.Flags(), fileName, name, strings.Join(signaturesFiles, " "), ) return executeWriteRetStdStreams(f.T, cmd) @@ -333,7 +337,7 @@ func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, // TxStakingCreateValidator is gaiacli tx staking create-validator func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey) + cmd := fmt.Sprintf("../../../build/gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey) cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05") cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10") cmd += fmt.Sprintf(" --min-self-delegation=%v", "1") @@ -342,7 +346,7 @@ func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk. // TxStakingUnbond is gaiacli tx staking unbond func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { - cmd := fmt.Sprintf("gaiacli tx staking unbond %s %v --from=%s %v", validator, shares, from, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli tx staking unbond %s %v --from=%s %v", validator, shares, from, f.Flags()) return executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -351,20 +355,20 @@ func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress // TxGovSubmitProposal is gaiacli tx gov submit-proposal func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ) + cmd := fmt.Sprintf("../../../build/gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ) cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxGovDeposit is gaiacli tx gov deposit func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags()) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxGovVote is gaiacli tx gov vote func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags()) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -373,7 +377,7 @@ func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, // QueryAccount is gaiacli query account func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.BaseAccount { - cmd := fmt.Sprintf("gaiacli query account %s %v", address, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query account %s %v", address, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var initRes map[string]json.RawMessage err := json.Unmarshal([]byte(out), &initRes) @@ -392,7 +396,7 @@ func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.Ba // QueryTxs is gaiacli query txs func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { - cmd := fmt.Sprintf("gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var txs []sdk.TxResponse cdc := app.MakeCodec() @@ -403,7 +407,7 @@ func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { // QueryTxsInvalid query txs with wrong parameters and compare expected error func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...string) { - cmd := fmt.Sprintf("gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) _, err := tests.ExecuteT(f.T, cmd, "") require.EqualError(f.T, expectedErr, err) } @@ -413,7 +417,7 @@ func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...s // QueryStakingValidator is gaiacli query staking validator func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string) staking.Validator { - cmd := fmt.Sprintf("gaiacli query staking validator %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query staking validator %s %v", valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var validator staking.Validator cdc := app.MakeCodec() @@ -424,7 +428,7 @@ func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string // QueryStakingUnbondingDelegationsFrom is gaiacli query staking unbonding-delegations-from func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []staking.UnbondingDelegation { - cmd := fmt.Sprintf("gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ubds []staking.UnbondingDelegation cdc := app.MakeCodec() @@ -435,7 +439,7 @@ func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, // QueryStakingDelegationsTo is gaiacli query staking delegations-to func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...string) []staking.Delegation { - cmd := fmt.Sprintf("gaiacli query staking delegations-to %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query staking delegations-to %s %v", valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var delegations []staking.Delegation cdc := app.MakeCodec() @@ -446,7 +450,7 @@ func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...st // QueryStakingPool is gaiacli query staking pool func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { - cmd := fmt.Sprintf("gaiacli query staking pool %v", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query staking pool %v", f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var pool staking.Pool cdc := app.MakeCodec() @@ -457,7 +461,7 @@ func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { // QueryStakingParameters is gaiacli query staking parameters func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { - cmd := fmt.Sprintf("gaiacli query staking params %v", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query staking params %v", f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var params staking.Params cdc := app.MakeCodec() @@ -471,7 +475,7 @@ func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { // QueryGovParamDeposit is gaiacli query gov param deposit func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { - cmd := fmt.Sprintf("gaiacli query gov param deposit %s", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov param deposit %s", f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var depositParam gov.DepositParams cdc := app.MakeCodec() @@ -482,7 +486,7 @@ func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { // QueryGovParamVoting is gaiacli query gov param voting func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { - cmd := fmt.Sprintf("gaiacli query gov param voting %s", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov param voting %s", f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var votingParam gov.VotingParams cdc := app.MakeCodec() @@ -493,7 +497,7 @@ func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { // QueryGovParamTallying is gaiacli query gov param tallying func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { - cmd := fmt.Sprintf("gaiacli query gov param tallying %s", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov param tallying %s", f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var tallyingParam gov.TallyParams cdc := app.MakeCodec() @@ -504,7 +508,7 @@ func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { // QueryGovProposals is gaiacli query gov proposals func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { - cmd := fmt.Sprintf("gaiacli query gov proposals %v", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov proposals %v", f.Flags()) stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") if strings.Contains(stderr, "No matching proposals found") { return gov.Proposals{} @@ -519,7 +523,7 @@ func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { // QueryGovProposal is gaiacli query gov proposal func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal { - cmd := fmt.Sprintf("gaiacli query gov proposal %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov proposal %d %v", proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var proposal gov.Proposal cdc := app.MakeCodec() @@ -530,7 +534,7 @@ func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposa // QueryGovVote is gaiacli query gov vote func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote { - cmd := fmt.Sprintf("gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var vote gov.Vote cdc := app.MakeCodec() @@ -541,7 +545,7 @@ func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...s // QueryGovVotes is gaiacli query gov votes func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { - cmd := fmt.Sprintf("gaiacli query gov votes %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov votes %d %v", proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var votes []gov.Vote cdc := app.MakeCodec() @@ -552,7 +556,7 @@ func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { // QueryGovDeposit is gaiacli query gov deposit func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit { - cmd := fmt.Sprintf("gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposit gov.Deposit cdc := app.MakeCodec() @@ -563,7 +567,7 @@ func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, fla // QueryGovDeposits is gaiacli query gov deposits func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit { - cmd := fmt.Sprintf("gaiacli query gov deposits %d %v", propsalID, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query gov deposits %d %v", propsalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposits []gov.Deposit cdc := app.MakeCodec() @@ -577,7 +581,7 @@ func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposi // QuerySigningInfo returns the signing info for a validator func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { - cmd := fmt.Sprintf("gaiacli query slashing signing-info %s %s", val, f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query slashing signing-info %s %s", val, f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() @@ -589,7 +593,7 @@ func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { // QuerySlashingParams is gaiacli query slashing params func (f *Fixtures) QuerySlashingParams() slashing.Params { - cmd := fmt.Sprintf("gaiacli query slashing params %s", f.Flags()) + cmd := fmt.Sprintf("../../../build/gaiacli query slashing params %s", f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 8bdd645e9..b440db8f9 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -6,11 +6,13 @@ import ( "os" "path" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/rakyll/statik/fs" "github.com/spf13/cobra" "github.com/spf13/viper" - amino "github.com/tendermint/go-amino" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" @@ -28,6 +30,7 @@ import ( dist "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" gv "github.com/cosmos/cosmos-sdk/x/gov" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + mintrest "github.com/cosmos/cosmos-sdk/x/mint/client/rest" sl "github.com/cosmos/cosmos-sdk/x/slashing" slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" st "github.com/cosmos/cosmos-sdk/x/staking" @@ -35,11 +38,13 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + crisisclient "github.com/cosmos/cosmos-sdk/x/crisis/client" distcmd "github.com/cosmos/cosmos-sdk/x/distribution" distClient "github.com/cosmos/cosmos-sdk/x/distribution/client" govClient "github.com/cosmos/cosmos-sdk/x/gov/client" - slashingClient "github.com/cosmos/cosmos-sdk/x/slashing/client" - stakingClient "github.com/cosmos/cosmos-sdk/x/staking/client" + mintclient "github.com/cosmos/cosmos-sdk/x/mint/client" + slashingclient "github.com/cosmos/cosmos-sdk/x/slashing/client" + stakingclient "github.com/cosmos/cosmos-sdk/x/staking/client" _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) @@ -67,8 +72,10 @@ func main() { mc := []sdk.ModuleClients{ govClient.NewModuleClient(gv.StoreKey, cdc), distClient.NewModuleClient(distcmd.StoreKey, cdc), - stakingClient.NewModuleClient(st.StoreKey, cdc), - slashingClient.NewModuleClient(sl.StoreKey, cdc), + stakingclient.NewModuleClient(st.StoreKey, cdc), + mintclient.NewModuleClient(mint.StoreKey, cdc), + slashingclient.NewModuleClient(sl.StoreKey, cdc), + crisisclient.NewModuleClient(sl.StoreKey, cdc), } rootCmd := &cobra.Command{ @@ -124,7 +131,10 @@ func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { ) for _, m := range mc { - queryCmd.AddCommand(m.GetQueryCmd()) + mQueryCmd := m.GetQueryCmd() + if mQueryCmd != nil { + queryCmd.AddCommand(mQueryCmd) + } } return queryCmd @@ -166,6 +176,7 @@ func registerRoutes(rs *lcd.RestServer) { staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + mintrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } func registerSwaggerUI(rs *lcd.RestServer) { diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index d6d88e0df..b515f5236 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -22,6 +22,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// gaiad custom flags +const flagAssertInvariantsBlockly = "assert-invariants-blockly" + +var assertInvariantsBlockly bool + func main() { cdc := app.MakeCodec() @@ -50,6 +55,8 @@ func main() { // prepare and add flags executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome) + rootCmd.PersistentFlags().BoolVar(&assertInvariantsBlockly, flagAssertInvariantsBlockly, + false, "Assert registered invariants on a blockly basis") err := executor.Execute() if err != nil { // handle with #870 @@ -59,7 +66,7 @@ func main() { func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { return app.NewGaiaApp( - logger, db, traceStore, true, + logger, db, traceStore, true, assertInvariantsBlockly, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), ) @@ -68,14 +75,15 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application func exportAppStateAndTMValidators( logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, ) (json.RawMessage, []tmtypes.GenesisValidator, error) { + if height != -1 { - gApp := app.NewGaiaApp(logger, db, traceStore, false) + gApp := app.NewGaiaApp(logger, db, traceStore, false, false) err := gApp.LoadHeight(height) if err != nil { return nil, nil, err } return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } - gApp := app.NewGaiaApp(logger, db, traceStore, true) + gApp := app.NewGaiaApp(logger, db, traceStore, true, false) return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } diff --git a/cmd/gaia/cmd/gaiareplay/main.go b/cmd/gaia/cmd/gaiareplay/main.go index 78cacec36..fd2c518a4 100644 --- a/cmd/gaia/cmd/gaiareplay/main.go +++ b/cmd/gaia/cmd/gaiareplay/main.go @@ -107,7 +107,7 @@ func run(rootDir string) { // Application fmt.Println("Creating application") myapp := app.NewGaiaApp( - ctx.Logger, appDB, traceStoreWriter, true, + ctx.Logger, appDB, traceStoreWriter, true, true, baseapp.SetPruning(store.PruneEverything), // nothing ) diff --git a/cmd/gaia/init/genesis_accts_test.go b/cmd/gaia/init/genesis_accts_test.go index 2c17acc54..74af5a42b 100644 --- a/cmd/gaia/init/genesis_accts_test.go +++ b/cmd/gaia/init/genesis_accts_test.go @@ -32,8 +32,8 @@ func TestAddGenesisAccount(t *testing.T) { args{ app.GenesisState{}, addr1, - sdk.Coins{}, - sdk.Coins{}, + sdk.NewCoins(), + sdk.NewCoins(), 0, 0, }, @@ -44,8 +44,8 @@ func TestAddGenesisAccount(t *testing.T) { args{ app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}}, addr1, - sdk.Coins{}, - sdk.Coins{}, + sdk.NewCoins(), + sdk.NewCoins(), 0, 0, }, @@ -56,8 +56,8 @@ func TestAddGenesisAccount(t *testing.T) { args{ app.GenesisState{}, addr1, - sdk.Coins{sdk.NewInt64Coin("stake", 50)}, - sdk.Coins{sdk.NewInt64Coin("stake", 100)}, + sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), + sdk.NewCoins(sdk.NewInt64Coin("stake", 100)), 0, 0, }, @@ -68,8 +68,8 @@ func TestAddGenesisAccount(t *testing.T) { args{ app.GenesisState{}, addr1, - sdk.Coins{sdk.NewInt64Coin("stake", 50)}, - sdk.Coins{sdk.NewInt64Coin("stake", 50)}, + sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), + sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), 1654668078, 1554668078, }, diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 6ce4badec..970c77e0d 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -47,6 +47,7 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "gentx", Short: "Generate a genesis tx carrying a self delegation", + Args: cobra.NoArgs, Long: fmt.Sprintf(`This command is an alias of the 'gaiad tx create-validator' command'. It creates a genesis piece carrying a self delegation with the @@ -88,6 +89,10 @@ following delegation and commission default parameters: return err } + if err = app.GaiaValidateGenesisState(genesisState); err != nil { + return err + } + kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome)) if err != nil { return err @@ -107,8 +112,12 @@ following delegation and commission default parameters: } } + website := viper.GetString(cli.FlagWebsite) + details := viper.GetString(cli.FlagDetails) + identity := viper.GetString(cli.FlagIdentity) + // Set flags for creating gentx - prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) + prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey, website, details, identity) // Fetch the amount of coins staked amount := viper.GetString(cli.FlagAmount) @@ -122,9 +131,17 @@ following delegation and commission default parameters: return err } - // Run gaiad tx create-validator txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) + + // XXX: Set the generate-only flag here after the CLI context has + // been created. This allows the from name/key to be correctly populated. + // + // TODO: Consider removing the manual setting of generate-only in + // favor of a 'gentx' flag in the create-validator command. + viper.Set(client.FlagGenerateOnly, true) + + // create a 'create-validator' message txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { return err @@ -188,6 +205,9 @@ following delegation and commission default parameters: "write the genesis transaction JSON document to the given file instead of the default location") cmd.Flags().String(cli.FlagIP, ip, "The node's public IP") cmd.Flags().String(cli.FlagNodeID, "", "The node's NodeID") + cmd.Flags().String(cli.FlagWebsite, "", "The validator's (optional) website") + cmd.Flags().String(cli.FlagDetails, "", "The validator's (optional) details") + cmd.Flags().String(cli.FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") cmd.Flags().AddFlagSet(cli.FsCommissionCreate) cmd.Flags().AddFlagSet(cli.FsMinSelfDelegation) cmd.Flags().AddFlagSet(cli.FsAmount) @@ -224,16 +244,20 @@ func accountInGenesis(genesisState app.GenesisState, key sdk.AccAddress, coins s return fmt.Errorf("account %s in not in the app_state.accounts array of genesis.json", key) } -func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID string, - valPubKey crypto.PubKey) { - viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home +func prepareFlagsForTxCreateValidator( + config *cfg.Config, nodeID, ip, chainID string, valPubKey crypto.PubKey, website, details, identity string, +) { + viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) viper.Set(client.FlagChainID, chainID) - viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from - viper.Set(cli.FlagNodeID, nodeID) // --node-id - viper.Set(cli.FlagIP, ip) // --ip - viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey - viper.Set(client.FlagGenerateOnly, true) // --genesis-format - viper.Set(cli.FlagMoniker, config.Moniker) // --moniker + viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) + viper.Set(cli.FlagNodeID, nodeID) + viper.Set(cli.FlagIP, ip) + viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) + viper.Set(cli.FlagMoniker, config.Moniker) + viper.Set(cli.FlagWebsite, website) + viper.Set(cli.FlagDetails, details) + viper.Set(cli.FlagIdentity, identity) + if config.Moniker == "" { viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) } diff --git a/cmd/gaia/init/gentx_test.go b/cmd/gaia/init/gentx_test.go new file mode 100644 index 000000000..d99a562fc --- /dev/null +++ b/cmd/gaia/init/gentx_test.go @@ -0,0 +1,85 @@ +package init + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + "github.com/spf13/viper" + "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" +) + +func Test_prepareFlagsForTxCreateValidator(t *testing.T) { + defer server.SetupViper(t)() + defer setupClientHome(t)() + config, err := tcmd.ParseConfig() + require.Nil(t, err) + logger := log.NewNopLogger() + ctx := server.NewContext(config, logger) + + valPubKey, _ := sdk.GetConsPubKeyBech32("cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a") + + type args struct { + config *cfg.Config + nodeID string + ip string + chainID string + valPubKey crypto.PubKey + website string + details string + identity string + } + + type extraParams struct { + amount string + commissionRate string + commissionMaxRate string + commissionMaxChangeRate string + minSelfDelegation string + } + + type testcase struct { + name string + args args + } + + runTest := func(t *testing.T, tt testcase, params extraParams) { + prepareFlagsForTxCreateValidator(tt.args.config, tt.args.nodeID, tt.args.ip, tt.args.chainID, tt.args.valPubKey, tt.args.website, tt.args.details, tt.args.identity) + require.Equal(t, tt.args.website, viper.GetString(cli.FlagWebsite)) + require.Equal(t, tt.args.details, viper.GetString(cli.FlagDetails)) + require.Equal(t, tt.args.identity, viper.GetString(cli.FlagIdentity)) + require.Equal(t, params.amount, viper.GetString(cli.FlagAmount)) + require.Equal(t, params.commissionRate, viper.GetString(cli.FlagCommissionRate)) + require.Equal(t, params.commissionMaxRate, viper.GetString(cli.FlagCommissionMaxRate)) + require.Equal(t, params.commissionMaxChangeRate, viper.GetString(cli.FlagCommissionMaxChangeRate)) + require.Equal(t, params.minSelfDelegation, viper.GetString(cli.FlagMinSelfDelegation)) + } + + tests := []testcase{ + {"No parameters", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "", "", ""}}, + {"Optional parameters fed", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "cosmos.network", "details", "identity"}}, + } + + defaultParams := extraParams{defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Run(tt.name, func(t *testing.T) { runTest(t, tt, defaultParams) }) + }) + } + + // Override default params + params := extraParams{"5stake", "1.0", "1.0", "1.0", "1.0"} + viper.Set(cli.FlagAmount, params.amount) + viper.Set(cli.FlagCommissionRate, params.commissionRate) + viper.Set(cli.FlagCommissionMaxRate, params.commissionMaxRate) + viper.Set(cli.FlagCommissionMaxChangeRate, params.commissionMaxChangeRate) + viper.Set(cli.FlagMinSelfDelegation, params.minSelfDelegation) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { runTest(t, tt, params) }) + } +} diff --git a/cmd/gosum/main.go b/cmd/gosum/main.go new file mode 100644 index 000000000..f1c280051 --- /dev/null +++ b/cmd/gosum/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "crypto/sha256" + "fmt" + "io" + "log" + "os" +) + +// DONTCOVER + +// nolint: errcheck +func main() { + f, err := os.Open(os.Args[1]) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + log.Fatal(err) + } + + fmt.Printf("%x", h.Sum(nil)) +} diff --git a/cmd/sdkch/README.md b/cmd/sdkch/README.md new file mode 100644 index 000000000..204b67f55 --- /dev/null +++ b/cmd/sdkch/README.md @@ -0,0 +1,50 @@ +# sdkch +Simple tool to maintain modular changelogs + +# Usage + +``` +$ sdkch -help +usage: sdkch [-d directory] [-prune] command + +Maintain unreleased changelog entries in a modular fashion. + +Commands: + add section stanza [message] Add an entry file. + If message is empty, start the editor to edit + the message. + generate [version] Generate a changelog in Markdown format and print + it to STDOUT. version defaults to UNRELEASED. + + Sections Stanzas + --- --- + breaking gaia + features gaiacli +improvements gaiarest + bugfixes sdk + tendermint + +Flags: + -d string + entry files directory (default "/home/alessio/work/tendermint/src/github.com/cosmos/cosmos-sdk/.pending") + -prune + prune old entries after changelog generation +``` + +## Add a new entry + +You can either drop a text file in the appropriate directory or use the `add` command: + +```bash +$ sdkch add features gaiacli '#3452 New cool gaiacli command' +``` + +If no message is provided, a new entry file is opened in an editor is started + +## Generate the full changelog + +```bash +$ sdkch generate v0.30.0 +``` + +The `-prune` flag would remove the old entry files after the changelog is generated. diff --git a/cmd/sdkch/main.go b/cmd/sdkch/main.go new file mode 100644 index 000000000..df3a78145 --- /dev/null +++ b/cmd/sdkch/main.go @@ -0,0 +1,378 @@ +package main + +import ( + "bufio" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "math" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" +) + +const ( + entriesDirName = ".pending" + ghLinkPattern = `#([0-9]+)` + ghLinkExpanded = `[\#$1](https://github.com/cosmos/cosmos-sdk/issues/$1)` + maxEntryFilenameLength = 20 +) + +var ( + progName string + verboseLog *log.Logger + + entriesDir string + verboseLogging bool + + // sections name-title map + sections = map[string]string{ + "breaking": "Breaking Changes", + "features": "New features", + "improvements": "Improvements", + "bugfixes": "Bugfixes", + } + // stanzas name-title map + stanzas = map[string]string{ + "gaia": "Gaia", + "gaiacli": "Gaia CLI", + "gaiarest": "Gaia REST API", + "sdk": "SDK", + "tendermint": "Tendermint", + } +) + +func init() { + progName = filepath.Base(os.Args[0]) + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + flag.StringVar(&entriesDir, "d", filepath.Join(cwd, entriesDirName), "entry files directory") + flag.BoolVar(&verboseLogging, "v", false, "enable verbose logging") + flag.Usage = printUsage + +} + +func main() { + logPrefix := fmt.Sprintf("%s: ", filepath.Base(progName)) + log.SetFlags(0) + log.SetPrefix(logPrefix) + flag.Parse() + verboseLog = log.New(ioutil.Discard, "", 0) + if verboseLogging { + verboseLog.SetOutput(os.Stderr) + verboseLog.SetPrefix(logPrefix) + } + + if flag.NArg() < 1 { + errInsufficientArgs() + } + + cmd := flag.Arg(0) + switch cmd { + + case "add": + if flag.NArg() < 3 { + errInsufficientArgs() + } + if flag.NArg() > 4 { + errTooManyArgs() + } + sectionDir, stanzaDir := flag.Arg(1), flag.Arg(2) + validateSectionStanzaDirs(sectionDir, stanzaDir) + if flag.NArg() == 4 { + addSinglelineEntryFile(sectionDir, stanzaDir, strings.TrimSpace(flag.Arg(3))) + return + } + addEntryFile(sectionDir, stanzaDir) + + case "generate": + version := "UNRELEASED" + if flag.NArg() > 1 { + version = strings.Join(flag.Args()[1:], " ") + } + generateChangelog(version) + + case "prune": + pruneEmptyDirectories() + + default: + unknownCommand(cmd) + } +} + +func addSinglelineEntryFile(sectionDir, stanzaDir, message string) { + filename := filepath.Join( + filepath.Join(entriesDir, sectionDir, stanzaDir), + generateFileName(message), + ) + + writeEntryFile(filename, []byte(message)) +} + +func addEntryFile(sectionDir, stanzaDir string) { + bs := readUserInput() + firstLine := strings.TrimSpace(strings.Split(string(bs), "\n")[0]) + filename := filepath.Join( + filepath.Join(entriesDir, sectionDir, stanzaDir), + generateFileName(firstLine), + ) + + writeEntryFile(filename, bs) +} + +var filenameInvalidChars = regexp.MustCompile(`[^a-zA-Z0-9-_]`) + +func generateFileName(line string) string { + var chunks []string + subsWithInvalidCharsRemoved := strings.Split(filenameInvalidChars.ReplaceAllString(line, " "), " ") + for _, sub := range subsWithInvalidCharsRemoved { + sub = strings.TrimSpace(sub) + if len(sub) != 0 { + chunks = append(chunks, sub) + } + } + + ret := strings.Join(chunks, "-") + return ret[:int(math.Min(float64(len(ret)), float64(maxEntryFilenameLength)))] +} + +func directoryContents(dirPath string) []os.FileInfo { + contents, err := ioutil.ReadDir(dirPath) + if err != nil && !os.IsNotExist(err) { + log.Fatalf("couldn't read directory %s: %v", dirPath, err) + } + + if len(contents) == 0 { + return nil + } + + // Filter out hidden files + newContents := contents[:0] + for _, f := range contents { + if f.Name()[0] != '.' { // skip hidden files + newContents = append(newContents, f) + } + } + for i := len(newContents); i < len(contents); i++ { + contents[i] = nil + } + + return newContents +} + +func generateChangelog(version string) { + fmt.Printf("# %s\n\n", version) + for sectionDir, sectionTitle := range sections { + sectionTitlePrinted := false + for stanzaDir, stanzaTitle := range stanzas { + path := filepath.Join(entriesDir, sectionDir, stanzaDir) + files := directoryContents(path) + if len(files) == 0 { + continue + } + + if !sectionTitlePrinted { + fmt.Printf("## %s\n\n", sectionTitle) + sectionTitlePrinted = true + } + + fmt.Printf("### %s\n\n", stanzaTitle) + for _, f := range files { + verboseLog.Println("processing", f.Name()) + filename := filepath.Join(path, f.Name()) + if err := indentAndPrintFile(filename); err != nil { + log.Fatal(err) + } + } + + fmt.Println() + } + } +} + +func pruneEmptyDirectories() { + for sectionDir, _ := range sections { + for stanzaDir, _ := range stanzas { + mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir, stanzaDir)) + } + mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir)) + } +} + +// nolint: errcheck +func indentAndPrintFile(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + firstLine := true + ghLinkRe := regexp.MustCompile(ghLinkPattern) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if len(line) == 0 { + continue + } + + linkified := ghLinkRe.ReplaceAllString(line, ghLinkExpanded) + if firstLine { + fmt.Printf("* %s\n", linkified) + firstLine = false + continue + } + + fmt.Printf(" %s\n", linkified) + } + + return scanner.Err() +} + +// nolint: errcheck +func writeEntryFile(filename string, bs []byte) { + if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { + log.Fatal(err) + } + outFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + log.Fatal(err) + } + defer outFile.Close() + + if _, err := outFile.Write(bs); err != nil { + log.Fatal(err) + } + + log.Printf("Unreleased changelog entry written to: %s\n", filename) + log.Println("To modify this entry please edit or delete the above file directly.") +} + +func validateSectionStanzaDirs(sectionDir, stanzaDir string) { + if _, ok := sections[sectionDir]; !ok { + log.Fatalf("invalid section -- %s", sectionDir) + } + if _, ok := stanzas[stanzaDir]; !ok { + log.Fatalf("invalid stanza -- %s", stanzaDir) + } +} + +// nolint: errcheck +func readUserInput() []byte { + tempfilename, err := launchUserEditor() + if err != nil { + log.Fatalf("couldn't open an editor: %v", err) + } + defer os.Remove(tempfilename) + bs, err := ioutil.ReadFile(tempfilename) + if err != nil { + log.Fatalf("error: %v", err) + } + return bs +} + +// nolint: errcheck +func launchUserEditor() (string, error) { + editor, err := exec.LookPath("editor") + if err != nil { + editor = "" + } + if editor == "" { + editor = os.Getenv("VISUAL") + } + if editor == "" { + editor = os.Getenv("EDITOR") + } + if editor == "" { + return "", errors.New("no editor set, make sure that either " + + "VISUAL or EDITOR variables is set and pointing to a correct editor") + } + + tempfile, err := ioutil.TempFile("", "sdkch_*") + tempfilename := tempfile.Name() + if err != nil { + return "", err + } + tempfile.Close() + + cmd := exec.Command(editor, tempfilename) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + os.Remove(tempfilename) + return "", err + } + + fileInfo, err := os.Stat(tempfilename) + if err != nil { + os.Remove(tempfilename) + return "", err + } + if fileInfo.Size() == 0 { + log.Fatal("aborting due to empty message") + } + + return tempfilename, nil +} + +func mustPruneDirIfEmpty(path string) { + if contents := directoryContents(path); len(contents) == 0 { + if err := os.Remove(path); err != nil { + if !os.IsNotExist(err) { + log.Fatal(err) + } + return + } + log.Println(path, "removed") + } +} + +func printUsage() { + usageText := fmt.Sprintf(`usage: %s [-d directory] [-v] command + +Maintain unreleased changelog entries in a modular fashion. + +Commands: + add [section] [stanza] [message] Add an entry file. If message is empty, start + the editor to edit the message. + generate [version] Generate a changelog in Markdown format and print + it to STDOUT. version defaults to UNRELEASED. + prune Delete empty sub-directories recursively. + + Sections Stanzas + --- --- + breaking gaia + features gaiacli +improvements gaiarest + bugfixes sdk + tendermint +`, progName) + fmt.Fprintf(os.Stderr, "%s\nFlags:\n", usageText) + flag.PrintDefaults() +} + +func errInsufficientArgs() { + log.Println("insufficient arguments") + printUsage() + os.Exit(1) +} + +func errTooManyArgs() { + log.Println("too many arguments") + printUsage() + os.Exit(1) +} + +func unknownCommand(cmd string) { + log.Fatalf("unknown command -- '%s'\nTry '%s -help' for more information.", cmd, progName) +} + +// DONTCOVER diff --git a/contrib/export/lib.py b/contrib/export/lib.py new file mode 100644 index 000000000..2e7457f75 --- /dev/null +++ b/contrib/export/lib.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + + +def init_default_argument_parser(prog_desc, default_chain_id, default_start_time): + parser = argparse.ArgumentParser(description=prog_desc) + parser.add_argument( + 'exported_genesis', + help='exported genesis.json file', + type=argparse.FileType('r'), default=sys.stdin, + ) + parser.add_argument('--chain-id', type=str, default=default_chain_id) + parser.add_argument('--start-time', type=str, default=default_start_time) + return parser + + +def main(argument_parser, process_genesis_func): + args = argument_parser.parse_args() + if args.chain_id.strip() == '': + sys.exit('chain-id required') + + genesis = json.loads(args.exported_genesis.read()) + + print(json.dumps(process_genesis_func( + genesis=genesis, parsed_args=args,), indent=True)) diff --git a/contrib/export/v0.33.x-to-v0.34.0.py b/contrib/export/v0.33.x-to-v0.34.0.py new file mode 100755 index 000000000..bb7d6520c --- /dev/null +++ b/contrib/export/v0.33.x-to-v0.34.0.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import lib + + +def process_raw_genesis(genesis, parsed_args): + # update genesis with breaking changes + genesis['consensus_params']['block'] = genesis['consensus_params']['block_size'] + del genesis['consensus_params']['block_size'] + + genesis['app_state']['crisis'] = { + 'constant_fee': { + 'amount': '1333000000', # ~$5,000 worth of uatoms + 'denom': 'uatom', + }, + } + + # default tm value + genesis['consensus_params']['block']['time_iota_ms'] = '1000' + + # proposal #1 updates + genesis['app_state']['mint']['params']['blocks_per_year'] = '4855015' + + # proposal #2 updates + genesis['consensus_params']['block']['max_gas'] = '2000000' + genesis['consensus_params']['block']['max_bytes'] = '200000' + + # enable transfers + genesis['app_state']['bank']['send_enabled'] = True + genesis['app_state']['distr']['withdraw_addr_enabled'] = True + + # Set new chain ID and genesis start time + genesis['chain_id'] = parsed_args.chain_id.strip() + genesis['genesis_time'] = parsed_args.start_time + + return genesis + + +if __name__ == '__main__': + parser = lib.init_default_argument_parser( + prog_desc='Convert genesis.json from v0.33.x to v0.34.0', + default_chain_id='cosmoshub-n', + default_start_time='2019-02-11T12:00:00Z', + ) + lib.main(parser, process_raw_genesis) diff --git a/contrib/install-golangci-lint.sh b/contrib/install-golangci-lint.sh new file mode 100644 index 000000000..9547f7db7 --- /dev/null +++ b/contrib/install-golangci-lint.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -euo pipefail + +installer="$(mktemp)" +trap "rm -f ${installer}" EXIT + +GOBIN="${1}" +VERSION="${2}" +HASHSUM="${3}" +CURL="$(which curl)" +GOSUM="$(which gosum)" + +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}" = "$(${GOSUM} ${installer})" ] +chmod +x "${installer}" + +echo "Launching installer ..." >&2 +exec "${installer}" -d -b "${GOBIN}" "${VERSION}" diff --git a/crypto/keys/lazy_keybase_test.go b/crypto/keys/lazy_keybase_test.go new file mode 100644 index 000000000..face5edef --- /dev/null +++ b/crypto/keys/lazy_keybase_test.go @@ -0,0 +1,344 @@ +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 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 := "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("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + 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) + 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 := "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 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 TestLazyExportImportPubKey(t *testing.T) { + dir, cleanup := tests.NewTestCaseDir(t) + defer cleanup() + kb := New("keybasename", dir) + + // CreateMnemonic a private-public key pair and ensure consistency + notPasswd := "n9y25ah7" + info, _, err := kb.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 := 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 := "1234", "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 := "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, 0) + newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) + 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/mintkey/mintkey_test.go b/crypto/keys/mintkey/mintkey_test.go new file mode 100644 index 000000000..86df52366 --- /dev/null +++ b/crypto/keys/mintkey/mintkey_test.go @@ -0,0 +1,36 @@ +package mintkey_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" + "github.com/stretchr/testify/require" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +func TestArmorUnarmorPrivKey(t *testing.T) { + priv := secp256k1.GenPrivKey() + armor := mintkey.EncryptArmorPrivKey(priv, "passphrase") + _, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase") + require.Error(t, err) + decrypted, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase") + require.NoError(t, err) + 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) + armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()) + pubBytes, err := mintkey.UnarmorPubKeyBytes(armor) + require.NoError(t, err) + pub, err := cryptoAmino.PubKeyFromBytes(pubBytes) + require.NoError(t, err) + require.True(t, pub.Equals(info.GetPubKey())) +} diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index e8ee111d6..52dfd3a2b 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -34,21 +34,22 @@ module.exports = { ] }, { - title: "Gaia", + title: "Cosmos Hub", collapsable: true, children: [ - "/gaia/what-is-gaia", - "/gaia/installation", - "/gaia/join-mainnet", - "/gaia/validators/validator-setup", - "/gaia/validators/overview", - "/gaia/validators/security", - "/gaia/validators/validator-faq", - "/gaia/delegator-guide-cli", - "/gaia/ledger", - "/gaia/gaiacli", - "/gaia/join-testnet", - "/gaia/deploy-testnet" + "/cosmos-hub/what-is-gaia", + "/cosmos-hub/installation", + "/cosmos-hub/join-mainnet", + "/cosmos-hub/validators/validator-setup", + "/cosmos-hub/validators/overview", + "/cosmos-hub/validators/security", + "/cosmos-hub/validators/validator-faq", + "/cosmos-hub/delegator-guide-cli", + "/cosmos-hub/genesis", + "/cosmos-hub/ledger", + "/cosmos-hub/gaiacli", + "/cosmos-hub/join-testnet", + "/cosmos-hub/deploy-testnet" ] }, { diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index 5870d3e27..e858e4e73 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -95,7 +95,7 @@ then navigate to localhost:8080 in your browser. ## Build RPC Docs -First, run `make get_tools` from the root of repo, to install the swagger-ui tool. +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/develop/client/lcd/swagger-ui/swagger.yaml) diff --git a/docs/_attic/WIP-lamborghini-distribution/transactions.md b/docs/_attic/WIP-lamborghini-distribution/transactions.md index 085f3b6e3..b02141185 100644 --- a/docs/_attic/WIP-lamborghini-distribution/transactions.md +++ b/docs/_attic/WIP-lamborghini-distribution/transactions.md @@ -76,7 +76,7 @@ When a validator wishes to withdraw their transaction fees it must send 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 rewards, as well as any rewards -earning on their self-delegation. +earning on their self-delegation. ```golang type TxWithdrawValidator struct { diff --git a/docs/_attic/sdk/core/app1.md b/docs/_attic/sdk/core/app1.md index 4b2257b3c..88e75e17b 100644 --- a/docs/_attic/sdk/core/app1.md +++ b/docs/_attic/sdk/core/app1.md @@ -153,11 +153,11 @@ Along with the message, the Handler takes environmental information (a `Context` All information necessary for processing a message should be available in the context. Where is the KVStore in all of this? Access to the KVStore in a message handler is restricted by the Context via object-capability keys. -Only handlers which were given explict access to a store's key will be able to access that store during message processsing. +Only handlers which were given explicit access to a store's key will be able to access that store during message processsing. ### Context -The SDK uses a `Context` to propogate common information across functions. +The SDK uses a `Context` to propagate common information across functions. Most importantly, the `Context` restricts access to KVStores based on object-capability keys. Only handlers which have been given explicit access to a key will be able to access the corresponding store. @@ -176,7 +176,7 @@ func newFooHandler(key sdk.StoreKey) sdk.Handler { `Context` is modeled after the Golang [context.Context](https://golang.org/pkg/context/), which has become ubiquitous in networking middleware and routing applications as a means -to easily propogate request context through handler functions. +to easily propagate request context through handler functions. Many methods on SDK objects receive a context as the first argument. The Context also contains the diff --git a/docs/clients/lite/getting_started.md b/docs/clients/lite/getting_started.md index 47ee16f9e..7f5099402 100644 --- a/docs/clients/lite/getting_started.md +++ b/docs/clients/lite/getting_started.md @@ -19,18 +19,4 @@ gaiacli rest-server --chain-id=test \ --trust-node=false ``` -The server listens on HTTP by default. You can enable the secure layer by adding the `--tls` flag. -By default a self-signed certificate will be generated and its fingerprint printed out. You can -configure the server to use a SSL certificate by passing the certificate and key files via the -`--ssl-certfile` and `--ssl-keyfile` flags: - -```bash -gaiacli rest-server --chain-id=test \ - --laddr=tcp://localhost:1317 \ - --node tcp://localhost:26657 \ - --trust-node=false \ - --tls \ - --ssl-certfile=mycert.pem --ssl-keyfile=mykey.key -``` - For more information about the Gaia-Lite RPC, see the [swagger documentation](https://cosmos.network/rpc/) diff --git a/docs/clients/service-providers.md b/docs/clients/service-providers.md index 68024d641..b563153f1 100644 --- a/docs/clients/service-providers.md +++ b/docs/clients/service-providers.md @@ -121,3 +121,69 @@ mechanism for instance. In order to generate an unsigned transaction (example with [coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)), you need to use the field `generate_only` in the body of `base_req`. + +## Cosmos SDK Transaction Signing + +Cosmos SDK transaction signing is a fairly simple process. + +Every Cosmos SDK transaction has a canonical JSON representation. The `gaiacli` +and Stargate REST interfaces provide canonical JSON representations of transactions +and their "broadcast" functions will provide compact Amino (a protobuf-like wire format) +encoding translations. + +Things to know when signing messages: + +The format is as follows + +```json +{ + "account_number": XXX, + "chain_id": XXX, + "fee": XXX, + "sequence": XXX, + "memo": XXX, + "msgs": XXX +} +``` + +The signer must supply `"chain_id"`, `"account number"` and `"sequence number"`. + +The `"fee"`, `"msgs"` and `"memo"` fields will be supplied by the transaction +composer interface. + +The `"account_number"` and `"sequence"` fields can be queried directly from the +blockchain or cached locally. Getting these numbers wrong, along with the chainID, +is a common cause of invalid signature error. 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. + +Before signing, all keys are lexicographically sorted and all white space is +removed from the JSON output. + +The signature encoding is the 64-byte concatenation of ECDSArands (i.e. `r || s`), +where `s` is lexicographically less than its inverse in order to prevent malleability. +This is like Ethereum, but without the extra byte for PubKey recovery, since +Tendermint assumes the PubKey is always provided anyway. + +Signatures and public key examples in a signed transaction: + +``` json +{ + "type": "auth/StdTx", + "value": { + "msg": [...], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": XXX + }, + "signature": XXX + } + ], + } +} +``` + +Once signatures are properly generated, insert the JSON into into the generated +transaction and then use the broadcast endpoint. diff --git a/docs/gaia/README.md b/docs/cosmos-hub/README.md similarity index 73% rename from docs/gaia/README.md rename to docs/cosmos-hub/README.md index eaf21a823..75a9b31f4 100644 --- a/docs/gaia/README.md +++ b/docs/cosmos-hub/README.md @@ -1,6 +1,6 @@ -# Gaia Documentation +# Cosmos Hub Documentation -Welcome to the `Gaia` docs. `Gaia` is the current name of the Cosmos SDK application for the Cosmos Hub. +Welcome to the documentation of the **Cosmos Hub application: `gaia`**. ## Join the Cosmos Hub Mainnet @@ -12,11 +12,11 @@ Welcome to the `Gaia` docs. `Gaia` is the current name of the Cosmos SDK applica - [Join the testnet](./join-testnet.md) -## Setup your own `gaia` testnet +## Setup Your Own `gaia` Testnet - [Setup your own `gaia` testnet](./deploy-testnet.md) -## Additional resources +## Additional Resources - [Intro to validators](./validators/overview.md) - [Validator FAQ](./validators/validator-faq.md) diff --git a/docs/gaia/delegator-guide-cli.md b/docs/cosmos-hub/delegator-guide-cli.md similarity index 92% rename from docs/gaia/delegator-guide-cli.md rename to docs/cosmos-hub/delegator-guide-cli.md index a77914c1e..aab15da88 100644 --- a/docs/gaia/delegator-guide-cli.md +++ b/docs/cosmos-hub/delegator-guide-cli.md @@ -27,22 +27,22 @@ of any kind. Please exercise extreme caution! -## Table of contents +## Table of Contents - [Installing `gaiacli`](#installing-gaiacli) - [Cosmos Accounts](#cosmos-accounts) - + [Restoring an account from the fundrasier](#restoring-an-account-from-the-fundraiser) - + [Creating an account](#creating-an-account) -- [Accessing the Cosmos Hub network](#accessing-the-cosmos-hub-network) - + [Running your own full-node](#running-your-own-full-node) - + [Connecting to a remote full-node](#connecting-to-a-remote-full-node) -- [Setting up `gaiacli`](#setting-up-gaiacli) -- [Querying the state](#querying-the-state) + + [Restoring an Account from the Fundraiser](#restoring-an-account-from-the-fundraiser) + + [Creating an Account](#creating-an-account) +- [Accessing the Cosmos Hub Network](#accessing-the-cosmos-hub-network) + + [Running Your Own Full-Node](#running-your-own-full-node) + + [Connecting to a Remote Full-Node](#connecting-to-a-remote-full-node) +- [Setting Up `gaiacli`](#setting-up-gaiacli) +- [Querying the State](#querying-the-state) - [Sending Transactions](#sending-transactions) - + [A note on gas and fees](#a-note-on-gas-and-fees) - + [Bonding Atoms and Withdrawing rewards](#bonding-atoms-and-withdrawing-rewards) + + [A Note on Gas and Fees](#a-note-on-gas-and-fees) + + [Bonding Atoms and Withdrawing Rewards](#bonding-atoms-and-withdrawing-rewards) + [Participating in Governance](#participating-in-governance) - + [Signing transactions from an offline computer](#signing-transactions-from-an-offline-computer) + + [Signing Transactions from an Offline Computer](#signing-transactions-from-an-offline-computer) ## Installing `gaiacli` @@ -109,7 +109,7 @@ The funds stored in an account are controlled by the private key. This private k The address is a public string with a human-readable prefix (e.g. `cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg`) that identifies your account. When someone wants to send you funds, they send it to your address. It is computationally infeasible to find the private key associated with a given address. -### Restoring an account from the fundraiser +### Restoring an Account from the Fundraiser ::: tip *NOTE: This section only concerns fundraiser participants* @@ -117,7 +117,7 @@ The address is a public string with a human-readable prefix (e.g. `cosmos10snjt8 If you participated in the fundraiser, you should be in possession of a 12-words mnemonic. Newly generated mnemonics use 24 words, but 12-word mnemonics are also compatible with all the Cosmos tools. -#### On a ledger device +#### On a Ledger Device At the core of a ledger device, there is a mnemonic used to generate accounts on multiple blockchains (including the Cosmos Hub). Usually, you will create a new mnemonic when you initialize your ledger device. However, it is possible to tell the ledger device to use a mnemonic provided by the user instead. Let us go ahead and see how you can input the mnemonic you obtained during the fundraiser as the seed of your ledger device. @@ -138,7 +138,7 @@ Your ledger is now correctly set up with your fundraiser mnemonic! Do not lose t Next, click [here](#using-a-ledger-device) to learn how to generate an account. -#### On a computer +#### On a Computer ::: warning **NOTE: It is more secure to perform this action on an offline computer** @@ -155,11 +155,11 @@ You will be prompted to input a passphrase that is used to encrypt the private k - `` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction. - You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated. -### Creating an account +### Creating an Account -To create an account, you just need to have `gaiacli` installed. Before creating it, you need to know where you intend to store and interract with your private keys. The best options are to store them in an offline dedicated computer or a ledger device. Storing them on your regular online computer involves more risk, since anyone who infiltrates your computer through the internet could exfiltrate your private keys and steal your funds. +To create an account, you just need to have `gaiacli` installed. Before creating it, you need to know where you intend to store and interact with your private keys. The best options are to store them in an offline dedicated computer or a ledger device. Storing them on your regular online computer involves more risk, since anyone who infiltrates your computer through the internet could exfiltrate your private keys and steal your funds. -#### Using a ledger device +#### Using a Ledger Device ::: warning **Only use Ledger devices that you bought factory new or trust fully** @@ -185,7 +185,7 @@ gaiacli keys add --ledger - `` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction. - You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated. -#### Using a computer +#### Using a Computer ::: warning **NOTE: It is more secure to perform this action on an offline computer** @@ -225,7 +225,7 @@ gaiacli keys add --recover --account 1 This command will prompt you to input a passphrase as well as your mnemonic. Change the account number to generate a different account. -## Accessing the Cosmos Hub network +## Accessing the Cosmos Hub Network In order to query the state and send transactions, you need a way to access the network. To do so, you can either run your own full-node, or connect to someone else's. @@ -233,19 +233,19 @@ In order to query the state and send transactions, you need a way to access the **NOTE: Do not share your mnemonic (12 or 24 words) with anyone. The only person who should ever need to know it is you. This is especially important if you are ever approached via email or direct message by someone requesting that you share your mnemonic for any kind of blockchain services or support. No one from Cosmos, the Tendermint team or the Interchain Foundation will ever send an email that asks for you to share any kind of account credentials or your mnemonic."**. ::: -### Running your own full-node +### Running Your Own Full-Node This is the most secure option, but comes with relatively high resource requirements. In order to run your own full-node, you need good bandwidth and at least 1TB of disk space. You will find the tutorial on how to install `gaiad` [here](https://cosmos.network/docs/gaia/installation.html), and the guide to run a full-node [here](https://cosmos.network/docs/gaia/join-mainnet.html). -### Connecting to a remote full-node +### Connecting to a Remote Full-Node If you do not want or cannot run your own node, you can connect to someone else's full-node. You should pick an operator you trust, because a malicious operator could return incorrect query results or censor your transactions. However, they will never be able to steal your funds, as your private keys are stored locally on your computer or ledger device. Possible options of full-node operators include validators, wallet providers or exchanges. In order to connect to the full-node, you will need an address of the following form: `https://77.87.106.33:26657` (*Note: This is a placeholder*). This address has to be communicated by the full-node operator you choose to trust. You will use this address in the [following section](#setting-up-gaiacli). -## Setting up `gaiacli` +## Setting Up `gaiacli` ::: tip **Before setting up `gaiacli`, make sure you have set up a way to [access the Cosmos Hub network](#accessing-the-cosmos-hub-network)** @@ -289,7 +289,7 @@ Finally, let us set the `chain-id` of the blockchain we want to interact with: gaiacli config chain-id cosmoshub-1 ``` -## Querying the state +## Querying the State ::: tip **Before you can bond atoms and withdraw rewards, you need to [set up `gaiacli`](#setting-up-gaiacli)** @@ -337,10 +337,10 @@ For each command, you can use the `-h` or `--help` flag to get more information. ## Sending Transactions ::: warning -On Cosmos Hub mainnet, the accepted denom is `uatom` (micro-Atom), where `1atom = 1,000,000uatom` +On Cosmos Hub mainnet, the accepted denom is `uatom`, where `1atom = 1,000,000uatom` ::: -### A note on gas and fees +### A Note on Gas and Fees Transactions on the Cosmos Hub network need to include a transaction fee in order to be processed. This fee pays for the gas required to run the transaction. The formula is the following: @@ -358,14 +358,14 @@ The transaction `fees` are the product of `gas` and `gasPrice`. As a user, you h For mainnet, the recommended `gas-prices` is `0.025uatom`. ::: -### Bonding Atoms and Withdrawing rewards +### Bonding Atoms and Withdrawing Rewards ::: tip **Before you can bond atoms and withdraw rewards, you need to [set up `gaiacli`](#setting-up-gaiacli) and [create an account](#creating-an-account)** ::: ::: warning -**Before bonding Atoms, please read the [delegator faq](https://cosmos.network/resources/delegators) to understand the risk and responsabilities involved with delegating** +**Before bonding Atoms, please read the [delegator faq](https://cosmos.network/resources/delegators) to understand the risk and responsibilities involved with delegating** ::: ::: warning @@ -379,6 +379,14 @@ For mainnet, the recommended `gas-prices` is `0.025uatom`. gaiacli tx staking delegate --from --gas auto --gas-adjustment 1.5 --gas-prices +// Redelegate a certain amount of Atoms from a validator to another +// Can only be used if already bonded to a validator +// Redelegation takes effect immediately, there is no waiting period to redelegate +// After a redelegation, no other redelegation can be made from the account for the next 3 weeks +// ex value for flags: =cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, =100000000uatom, =0.025uatom + +gaiacli tx staking redelegate --from --gas auto --gas-adjustment 1.5 --gas-prices + // Withdraw all rewards // ex value for flag: =0.025uatom @@ -413,9 +421,9 @@ gaiacli query tx Double check with a block explorer if you interact with the network through a trusted full-node. -## Participating in governance +## Participating in Governance -#### Primer on governance +#### Primer on Governance The Cosmos Hub has a built-in governance system that lets bonded Atom holders vote on proposals. There are three types of proposal: @@ -429,7 +437,7 @@ Once the `deposit` reaches `minDeposit`, the proposal enters the `voting_period` At the end of the voting period, the proposal is accepted if there are more than 50% `Yes` votes (excluding `Abstain ` votes) and less than 33.33% of `NoWithVeto` votes (excluding `Abstain` votes). -#### In practice +#### In Practice ::: tip **Before you can bond atoms and withdraw rewards, you need to [bond Atoms](#bonding-atoms-and-withdrawing-rewards)** @@ -459,7 +467,7 @@ gaiacli tx gov deposit --gas auto --gas-adjustment 1.5 -- gaiacli tx gov vote