diff --git a/.circleci/config.yml b/.circleci/config.yml index 2f61bf4448..e4afa15f08 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -196,7 +196,8 @@ jobs: command: | export PATH="$GOBIN:$PATH" export GO111MODULE=on - scripts/multisim.sh 500 50 TestFullGaiaSimulation + make runsim + runsim 500 50 TestFullGaiaSimulation test_sim_gaia_multi_seed: <<: *linux_defaults @@ -214,7 +215,8 @@ jobs: command: | export PATH="$GOBIN:$PATH" export GO111MODULE=on - scripts/multisim.sh 50 10 TestFullGaiaSimulation + make runsim + runsim 50 10 TestFullGaiaSimulation test_cover: <<: *linux_defaults diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f2a5ac8a5..ea3db3d72e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers -* @alessio @alexanderbez @cwgoes @jackzampolin +* @rigelrozanski @alexanderbez @jackzampolin @alessio @cwgoes diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 7140f56da0..49cd078376 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -8,17 +8,25 @@ about: Create a report to help us squash bugs! v ✰ Thanks for opening an issue! ✰ v Before smashing the submit button please review the template. v Please also ensure that this is not a duplicate issue :) -☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> +☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> -## Summary of Bug - +## Summary of Bug + + + +## Version + + ## Steps to Reproduce - + + ____ + #### For Admin Use - - [ ] Not duplicate issue - - [ ] Appropriate labels applied - - [ ] Appropriate contributors tagged - - [ ] Contributor assigned/self-assigned + +- [ ] Not duplicate issue +- [ ] Appropriate labels applied +- [ ] Appropriate contributors tagged +- [ ] Contributor assigned/self-assigned diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 09fbb26d1c..324ab9a8ab 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -9,23 +9,28 @@ v ✰ Thanks for opening an issue! ✰ v Before smashing the submit button please review the template. v Word of caution: poorly thought-out proposals may be rejected v without deliberation -☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> +☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> ## Summary - + + ## Problem Definition + ## Proposal - + + ____ + #### For Admin Use - - [ ] Not duplicate issue - - [ ] Appropriate labels applied - - [ ] Appropriate contributors tagged - - [ ] Contributor assigned/self-assigned + +- [ ] Not duplicate issue +- [ ] Appropriate labels applied +- [ ] Appropriate contributors tagged +- [ ] Contributor assigned/self-assigned diff --git a/.pending/breaking/gaiacli/3715-query-distr-rew b/.pending/breaking/gaiacli/3715-query-distr-rew new file mode 100644 index 0000000000..5303400b68 --- /dev/null +++ b/.pending/breaking/gaiacli/3715-query-distr-rew @@ -0,0 +1,2 @@ +#3715 query distr rewards returns per-validator +rewards along with rewards total amount. diff --git a/.pending/breaking/gaiacli/4142-Turn-gaiacli-tx b/.pending/breaking/gaiacli/4142-Turn-gaiacli-tx new file mode 100644 index 0000000000..ed4c73b5bd --- /dev/null +++ b/.pending/breaking/gaiacli/4142-Turn-gaiacli-tx @@ -0,0 +1,2 @@ +#4142 Turn gaiacli tx send's --from into a required argument. +New shorter syntax: `gaiacli tx send FROM TO AMOUNT` diff --git a/.pending/breaking/gaiarest/3715-Update-distribu b/.pending/breaking/gaiarest/3715-Update-distribu new file mode 100644 index 0000000000..1c562efa45 --- /dev/null +++ b/.pending/breaking/gaiarest/3715-Update-distribu @@ -0,0 +1,3 @@ +#3715 Update /distribution/delegators/{delegatorAddr}/rewards GET endpoint +as per new specs. For a given delegation, the endpoint now returns the +comprehensive list of validator-reward tuples along with the grand total. diff --git a/.pending/breaking/gaiarest/4049-update-tag b/.pending/breaking/gaiarest/4049-update-tag new file mode 100644 index 0000000000..6a200b715b --- /dev/null +++ b/.pending/breaking/gaiarest/4049-update-tag @@ -0,0 +1 @@ +#4049 update tag MsgWithdrawValidatorCommission to match type \ No newline at end of file diff --git a/.pending/breaking/sdk/3775-unify-sender-tag b/.pending/breaking/sdk/3775-unify-sender-tag new file mode 100644 index 0000000000..94e7f702c5 --- /dev/null +++ b/.pending/breaking/sdk/3775-unify-sender-tag @@ -0,0 +1 @@ +#3775 unify sender transaction tag for ease of querying diff --git a/.pending/bugfixes/gaia/4113-Fix-incorrect-G b/.pending/bugfixes/gaia/4113-Fix-incorrect-G new file mode 100644 index 0000000000..2a5d1680b7 --- /dev/null +++ b/.pending/bugfixes/gaia/4113-Fix-incorrect-G @@ -0,0 +1 @@ +#4113 Fix incorrect `$GOBIN` in `Install Go` \ No newline at end of file diff --git a/.pending/bugfixes/gaiacli/3945-There-s-no-chec b/.pending/bugfixes/gaiacli/3945-There-s-no-chec new file mode 100644 index 0000000000..e8ab060541 --- /dev/null +++ b/.pending/bugfixes/gaiacli/3945-There-s-no-chec @@ -0,0 +1 @@ +#3945 There's no check for chain-id in TxBuilder.SignStdTx \ No newline at end of file diff --git a/.pending/bugfixes/sdk/3705-Return-instead-of-null-when-querying-delegator-rewards b/.pending/bugfixes/sdk/3705-Return-instead-of-null-when-querying-delegator-rewards new file mode 100644 index 0000000000..39497c2094 --- /dev/null +++ b/.pending/bugfixes/sdk/3705-Return-instead-of-null-when-querying-delegator-rewards @@ -0,0 +1 @@ +#3705 Return `[]` instead of `null` when querying delegator rewards. diff --git a/.pending/bugfixes/sdk/3966-fixed-multiple- b/.pending/bugfixes/sdk/3966-fixed-multiple- new file mode 100644 index 0000000000..8ffae93169 --- /dev/null +++ b/.pending/bugfixes/sdk/3966-fixed-multiple- @@ -0,0 +1,2 @@ +#3966 fixed multiple assigns to action tags +#3793 add delegator tag for MsgCreateValidator and deleted unused moniker and identity tags diff --git a/.pending/features/sdk/3981-Add-support-to-gracefully-halt-a-node-at-a-given-height b/.pending/features/sdk/3981-Add-support-to-gracefully-halt-a-node-at-a-given-height new file mode 100644 index 0000000000..2e6e3332ef --- /dev/null +++ b/.pending/features/sdk/3981-Add-support-to-gracefully-halt-a-node-at-a-given-height @@ -0,0 +1,2 @@ +#3981 Add support to gracefully halt a node at a given height +via the node's `halt-height` config or CLI value. diff --git a/.pending/improvements/gaia/4042-Add-description b/.pending/improvements/gaia/4042-Add-description new file mode 100644 index 0000000000..03ed9cc618 --- /dev/null +++ b/.pending/improvements/gaia/4042-Add-description @@ -0,0 +1 @@ +#4042 Update docs and scripts to include the correct `GO111MODULE=on` environment variable. diff --git a/.pending/improvements/gaia/4062-Remove-cmd-gaia b/.pending/improvements/gaia/4062-Remove-cmd-gaia new file mode 100644 index 0000000000..b0d5e7c9ec --- /dev/null +++ b/.pending/improvements/gaia/4062-Remove-cmd-gaia @@ -0,0 +1 @@ +#4066 Fix 'ExportGenesisFile() incorrectly overwrites genesis' diff --git a/.pending/improvements/gaia/4064-Remove-dep-and- b/.pending/improvements/gaia/4064-Remove-dep-and- new file mode 100644 index 0000000000..3792deb540 --- /dev/null +++ b/.pending/improvements/gaia/4064-Remove-dep-and- @@ -0,0 +1 @@ +#4064 Remove `dep` and `vendor` from `doc` and `version`. \ No newline at end of file diff --git a/.pending/improvements/gaia/4080-add-missing-inv b/.pending/improvements/gaia/4080-add-missing-inv new file mode 100644 index 0000000000..48c8172da0 --- /dev/null +++ b/.pending/improvements/gaia/4080-add-missing-inv @@ -0,0 +1 @@ +#4080 add missing invariants during simulations \ No newline at end of file diff --git a/.pending/improvements/gaiacli/3426-remove-redundant-account-check b/.pending/improvements/gaiacli/3426-remove-redundant-account-check new file mode 100644 index 0000000000..adf96e7675 --- /dev/null +++ b/.pending/improvements/gaiacli/3426-remove-redundant-account-check @@ -0,0 +1 @@ +#4068 Remove redundant account check on `gaiacli` diff --git a/.pending/improvements/gaiarest/2007-Return-200-status-code-on-empty-results b/.pending/improvements/gaiarest/2007-Return-200-status-code-on-empty-results new file mode 100644 index 0000000000..af20022008 --- /dev/null +++ b/.pending/improvements/gaiarest/2007-Return-200-status-code-on-empty-results @@ -0,0 +1 @@ +#2007 Return 200 status code on empty results diff --git a/.pending/improvements/gaiarest/4123-Fix-typo-url-er b/.pending/improvements/gaiarest/4123-Fix-typo-url-er new file mode 100644 index 0000000000..8f25a8f05e --- /dev/null +++ b/.pending/improvements/gaiarest/4123-Fix-typo-url-er @@ -0,0 +1 @@ +#4123 Fix typo, url error and outdated command description of doc clients. \ No newline at end of file diff --git a/.pending/improvements/gaiarest/4129-Translate-doc-c b/.pending/improvements/gaiarest/4129-Translate-doc-c new file mode 100644 index 0000000000..ab2710210f --- /dev/null +++ b/.pending/improvements/gaiarest/4129-Translate-doc-c @@ -0,0 +1 @@ +#4129 Translate doc clients to chinese. \ No newline at end of file diff --git a/.pending/improvements/sdk/3774-add-category-tag b/.pending/improvements/sdk/3774-add-category-tag new file mode 100644 index 0000000000..afeb256f63 --- /dev/null +++ b/.pending/improvements/sdk/3774-add-category-tag @@ -0,0 +1 @@ +#3774 add category tag to transactions for ease of filtering diff --git a/.pending/improvements/sdk/3914-Implement-invariant-benchmarks-and-add-target-to-makefile b/.pending/improvements/sdk/3914-Implement-invariant-benchmarks-and-add-target-to-makefile new file mode 100644 index 0000000000..46b548d304 --- /dev/null +++ b/.pending/improvements/sdk/3914-Implement-invariant-benchmarks-and-add-target-to-makefile @@ -0,0 +1 @@ +#3914 Implement invariant benchmarks and add target to makefile. diff --git a/.pending/improvements/sdk/3978-Return-ErrUnknownRequest-in-message-handlers-for-unknown b/.pending/improvements/sdk/3978-Return-ErrUnknownRequest-in-message-handlers-for-unknown new file mode 100644 index 0000000000..413689c05e --- /dev/null +++ b/.pending/improvements/sdk/3978-Return-ErrUnknownRequest-in-message-handlers-for-unknown @@ -0,0 +1,2 @@ +#3978 Return ErrUnknownRequest in message handlers for unknown +or invalid routed messages. diff --git a/.pending/improvements/tendermint/Upgrade-tendermint-t b/.pending/improvements/tendermint/Upgrade-tendermint-t new file mode 100644 index 0000000000..0359fd50ed --- /dev/null +++ b/.pending/improvements/tendermint/Upgrade-tendermint-t @@ -0,0 +1 @@ +#4138 Upgrade tendermint to v0.31.5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6cd69fb45..ed3cd6be42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ contributors, the general procedure for contributing has been established: make a comment on the issue to inform the community of your intentions to begin work, 4. follow standard github best practices: fork the repo, branch from the - tip of `develop`, make some commits, and submit a PR to `develop`, + top of `develop`, make some commits, and submit a PR to `develop`, 5. include `WIP:` in the PR-title to and submit your PR early, even if it's incomplete, this indicates to the community you're working on something and allows them to provide comments early in the development process. When the code @@ -32,12 +32,11 @@ taken place in a github issue, that PR runs a high likelihood of being rejected. Take a peek at our [coding repo](https://github.com/tendermint/coding) for overall information on repository workflow and standards. Note, we use `make -get_dev_tools` and `make update_dev_tools` for installing the linting tools. +tools` for installing the linting tools. Other notes: - Looking for a good place to start contributing? How about checking out some - [good first - issues](https://github.com/cosmos/cosmos-sdk/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) + [good first issues](https://github.com/cosmos/cosmos-sdk/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint` @@ -67,9 +66,9 @@ All PRs require two Reviews before merge (except docs changes, or variable name- If you open a PR on the Cosmos SDK, it is mandatory to update the relevant documentation in /docs. -* If your change relates to the core SDK (baseapp, store, ...), please update the docs/gaia folder, the docs/examples folder and possibly the docs/spec folder. -* If your changes relate specifically to the gaia application (not including modules), please modify the docs/gaia folder. -* If your changes relate to a module, please update the module's spec in docs/spec. If the module is used by gaia, you might also need to modify docs/gaia and/or docs/examples. +* If your change relates to the core SDK (baseapp, store, ...), please update the docs/cosmos-hub folder and possibly the docs/spec folder. +* If your changes relate specifically to the gaia application (not including modules), please modify the docs/cosmos-hub folder. +* If your changes relate to a module, please update the module's spec in docs/spec. If the module is used by gaia, you might also need to modify docs/cosmos-hub. * If your changes relate to the core of the CLI or Light-client (not specifically to module's CLI/Rest), please modify the docs/clients folder. ## Forking @@ -85,11 +84,11 @@ For instance, to create a fork and work on a branch of it, I would: - Create the fork on github, using the fork button. - Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/cosmos-sdk`) - `git remote rename origin upstream` - - `git remote add origin git@github.com:ebuchman/cosmos-sdk.git` + - `git remote add origin git@github.com:rigeyrigerige/cosmos-sdk.git` Now `origin` refers to my fork and `upstream` refers to the Cosmos-SDK version. So I can `git push -u origin master` to update my fork, and make pull requests to Cosmos-SDK from there. -Of course, replace `ebuchman` with your git handle. +Of course, replace `rigeyrigerige` with your git handle. To pull in updates from the origin repo, run @@ -132,7 +131,7 @@ The idea is you should be able to see the error message and figure out exactly what failed. Here is an example check: -``` +```go for tcIndex, tc := range cases { @@ -140,7 +139,7 @@ for tcIndex, tc := range cases { require.Equal(t, expectedTx[:32], calculatedTx[:32], "First 32 bytes of the txs differed. tc #%d, i #%d", tcIndex, i) - ``` +``` ## Branching Model and Release diff --git a/Makefile b/Makefile index 456f19cddf..ff5f742e6f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ +#!/usr/bin/make -f + PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') -CAT := $(if $(filter $(OS),Windows_NT),type,cat) LEDGER_ENABLED ?= true GOBIN ?= $(GOPATH)/bin GOSUM := $(shell which gosum) @@ -48,7 +49,7 @@ ldflags = -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ -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) +ldflags += -X github.com/cosmos/cosmos-sdk/version.GoSumHash=$(shell $(GOSUM) go.sum) endif ifeq ($(WITH_CLEVELDB),yes) @@ -62,7 +63,7 @@ BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' all: tools install lint test # The below include contains the tools target. -include scripts/Makefile +include contrib/devtools/Makefile ######################################## ### CI @@ -121,8 +122,6 @@ draw_deps: tools clean: rm -rf snapcraft-local.yaml build/ -distclean: clean - rm -rf vendor/ ######################################## ### Documentation @@ -166,22 +165,33 @@ test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." @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: +test_sim_gaia_import_export: runsim @echo "Running Gaia import/export simulation. This may take several minutes..." - @bash scripts/multisim.sh 50 5 TestGaiaImportExport + $(GOBIN)/runsim 50 5 TestGaiaImportExport -test_sim_gaia_simulation_after_import: +test_sim_gaia_simulation_after_import: runsim @echo "Running Gaia simulation-after-import. This may take several minutes..." - @bash scripts/multisim.sh 50 5 TestGaiaSimulationAfterImport + $(GOBIN)/runsim 50 5 TestGaiaSimulationAfterImport -test_sim_gaia_custom_genesis_multi_seed: +test_sim_gaia_custom_genesis_multi_seed: runsim @echo "Running multi-seed custom genesis simulation..." @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." - @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation ${HOME}/.gaiad/config/genesis.json + $(GOBIN)/runsim -g ${HOME}/.gaiad/config/genesis.json 400 5 TestFullGaiaSimulation -test_sim_gaia_multi_seed: +test_sim_gaia_multi_seed: runsim @echo "Running multi-seed Gaia simulation. This may take awhile!" - @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation + $(GOBIN)/runsim 400 5 TestFullGaiaSimulation + +test_sim_benchmark_invariants: + @echo "Running simulation invariant benchmarks..." + @go test -mod=readonly ./cmd/gaia/app -benchmem -bench=BenchmarkInvariants -run=^$ \ + -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationBlockSize=200 \ + -SimulationCommit=true -SimulationSeed=57 -v -timeout 24h + +# Don't move it into tools - this will be gone once gaia has moved into the new repo +runsim: $(GOBIN)/runsim +$(GOBIN)/runsim: cmd/gaia/contrib/runsim/main.go + go install github.com/cosmos/cosmos-sdk/cmd/gaia/contrib/runsim SIM_NUM_BLOCKS ?= 500 SIM_BLOCK_SIZE ?= 200 @@ -264,11 +274,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: install install_debug dist clean distclean \ +.PHONY: install install_debug dist clean \ 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 \ +test_sim_gaia_multi_seed test_sim_gaia_import_export test_sim_benchmark_invariants \ go-mod-cache diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index b244a98b77..87cd86b0cd 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -3,6 +3,7 @@ package baseapp import ( "fmt" "io" + "os" "reflect" "runtime/debug" "strings" @@ -81,6 +82,9 @@ type BaseApp struct { // flag for sealing options and parameters to a BaseApp sealed bool + + // height at which to halt the chain and gracefully shutdown + haltHeight uint64 } var _ abci.Application = (*BaseApp)(nil) @@ -230,6 +234,10 @@ func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) { app.minGasPrices = gasPrices } +func (app *BaseApp) setHaltHeight(height uint64) { + app.haltHeight = height +} + // Router returns the router of the BaseApp. func (app *BaseApp) Router() Router { if app.sealed { @@ -885,7 +893,13 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc return } -// Commit implements the ABCI interface. +// Commit implements the ABCI interface. It will commit all state that exists in +// the deliver state's multi-store and includes the resulting commit ID in the +// returned abci.ResponseCommit. Commit will set the check state based on the +// latest header and reset the deliver state. Also, if a non-zero halt height is +// defined in config, Commit will execute a deferred function call to check +// against that height and gracefully halt if it matches the latest committed +// height. func (app *BaseApp) Commit() (res abci.ResponseCommit) { header := app.deliverState.ctx.BlockHeader() @@ -896,13 +910,20 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Reset the Check state to the latest committed. // - // NOTE: safe because Tendermint holds a lock on the mempool for Commit. - // Use the header from this latest block. + // NOTE: This is safe because Tendermint holds a lock on the mempool for + // Commit. Use the header from this latest block. app.setCheckState(header) // empty/reset the deliver state app.deliverState = nil + defer func() { + if app.haltHeight > 0 && uint64(header.Height) == app.haltHeight { + app.logger.Info("halting node per configuration", "height", app.haltHeight) + os.Exit(0) + } + }() + return abci.ResponseCommit{ Data: commitID.Hash, } diff --git a/baseapp/options.go b/baseapp/options.go index a40d6eba56..d78f59958a 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -28,6 +28,11 @@ func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { return func(bap *BaseApp) { bap.setMinGasPrices(gasPrices) } } +// SetHaltHeight returns a BaseApp option function that sets the halt height. +func SetHaltHeight(height uint64) func(*BaseApp) { + return func(bap *BaseApp) { bap.setHaltHeight(height) } +} + func (app *BaseApp) SetName(name string) { if app.sealed { panic("SetName() on sealed BaseApp") diff --git a/client/config.go b/client/config.go index 04ec8f80df..4946a96ea8 100644 --- a/client/config.go +++ b/client/config.go @@ -16,6 +16,9 @@ import ( const ( flagGet = "get" + + // DefaultKeyPass contains the default key password for genesis transactions + DefaultKeyPass = "12345678" ) var configDefaults = map[string]string{ diff --git a/client/config_test.go b/client/config_test.go index 6263a212ac..1ff3b34f69 100644 --- a/client/config_test.go +++ b/client/config_test.go @@ -1,14 +1,15 @@ 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" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/cli" ) // For https://github.com/cosmos/cosmos-sdk/issues/3899 diff --git a/client/context/context.go b/client/context/context.go index ea87533ff4..0a42ff4afe 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -58,9 +58,10 @@ type CLIContext struct { SkipConfirm bool } -// NewCLIContext returns a new initialized CLIContext with parameters from the -// command line using Viper. -func NewCLIContext() CLIContext { +// NewCLIContextWithFrom returns a new initialized CLIContext with parameters from the +// command line using Viper. It takes a key name or address and populates the FromName and +// FromAddress field accordingly. +func NewCLIContextWithFrom(from string) CLIContext { var rpc rpcclient.Client nodeURI := viper.GetString(client.FlagNode) @@ -68,7 +69,6 @@ func NewCLIContext() CLIContext { rpc = rpcclient.NewHTTP(nodeURI, "/websocket") } - from := viper.GetString(client.FlagFrom) genOnly := viper.GetBool(client.FlagGenerateOnly) fromAddress, fromName, err := GetFromFields(from, genOnly) if err != nil { @@ -104,6 +104,10 @@ func NewCLIContext() CLIContext { } } +// NewCLIContext returns a new initialized CLIContext with parameters from the +// command line using Viper. +func NewCLIContext() CLIContext { return NewCLIContextWithFrom(viper.GetString(client.FlagFrom)) } + func createVerifier() tmlite.Verifier { trustNodeDefined := viper.IsSet(client.FlagTrustNode) if !trustNodeDefined { diff --git a/client/keys/add.go b/client/keys/add.go index 7ab5ecdb8a..390b1bc2a7 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -7,7 +7,6 @@ import ( "sort" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -102,7 +101,7 @@ func runAddCmd(_ *cobra.Command, args []string) error { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly kb = keys.NewInMemory() - encryptPassword = app.DefaultKeyPass + encryptPassword = client.DefaultKeyPass } else { kb, err = NewKeyBaseFromHomeFlag() if err != nil { diff --git a/client/keys/delete_test.go b/client/keys/delete_test.go index ad7b5367fd..6fda75cbdd 100644 --- a/client/keys/delete_test.go +++ b/client/keys/delete_test.go @@ -7,11 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/tests" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/tests" ) func Test_runDeleteCmd(t *testing.T) { diff --git a/client/keys/list.go b/client/keys/list.go index 19873aabb7..2e1ef1938a 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -1,8 +1,9 @@ package keys import ( - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" ) func listKeysCmd() *cobra.Command { diff --git a/client/keys/list_test.go b/client/keys/list_test.go index 9fdab6a751..fe0ac46341 100644 --- a/client/keys/list_test.go +++ b/client/keys/list_test.go @@ -3,9 +3,10 @@ package keys import ( "testing" - "github.com/cosmos/cosmos-sdk/tests" "github.com/stretchr/testify/assert" + "github.com/cosmos/cosmos-sdk/tests" + "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/cli" diff --git a/client/keys/show.go b/client/keys/show.go index eaa800e11f..131b45f5e7 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" diff --git a/client/keys/update_test.go b/client/keys/update_test.go index e1c6784f15..78f3363455 100644 --- a/client/keys/update_test.go +++ b/client/keys/update_test.go @@ -7,10 +7,11 @@ import ( "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/tests" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/tests" + "github.com/stretchr/testify/assert" ) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 55c773cfa1..69c5281b38 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -3,6 +3,7 @@ package lcd import ( "encoding/base64" "encoding/hex" + "encoding/json" "fmt" "net/http" "os" @@ -18,7 +19,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" clienttx "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,6 +28,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" dclcommon "github.com/cosmos/cosmos-sdk/x/distribution/client/common" distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -38,7 +39,7 @@ const ( name2 = "test2" name3 = "test3" memo = "LCD test tx" - pw = app.DefaultKeyPass + pw = client.DefaultKeyPass altPw = "12345678901" ) @@ -115,7 +116,7 @@ func TestCoinSend(t *testing.T) { // query empty res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", someFakeAddr), nil) - require.Equal(t, http.StatusNoContent, res.StatusCode, body) + require.Equal(t, http.StatusOK, res.StatusCode, body) acc := getAccount(t, port, addr) initialBalance := acc.GetCoins() @@ -337,7 +338,7 @@ func TestTxs(t *testing.T) { txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String())) require.Equal(t, emptyTxs, txs) - txs = getTransactions(t, port, fmt.Sprintf("action=submit%%20proposal&proposer=%s", addr.String())) + txs = getTransactions(t, port, fmt.Sprintf("action=submit%%20proposal&sender=%s", addr.String())) require.Equal(t, emptyTxs, txs) // create tx @@ -456,7 +457,7 @@ func TestBonding(t *testing.T) { // query tx txs := getTransactions(t, port, - fmt.Sprintf("action=delegate&delegator=%s", addr), + fmt.Sprintf("action=delegate&sender=%s", addr), fmt.Sprintf("destination-validator=%s", operAddrs[0]), ) require.Len(t, txs, 1) @@ -509,7 +510,7 @@ func TestBonding(t *testing.T) { // query tx txs = getTransactions(t, port, - fmt.Sprintf("action=begin_unbonding&delegator=%s", addr), + fmt.Sprintf("action=begin_unbonding&sender=%s", addr), fmt.Sprintf("source-validator=%s", operAddrs[0]), ) require.Len(t, txs, 1) @@ -546,7 +547,7 @@ func TestBonding(t *testing.T) { // query tx txs = getTransactions(t, port, - fmt.Sprintf("action=begin_redelegate&delegator=%s", addr), + fmt.Sprintf("action=begin_redelegate&sender=%s", addr), fmt.Sprintf("source-validator=%s", operAddrs[0]), fmt.Sprintf("destination-validator=%s", operAddrs[1]), ) @@ -674,7 +675,7 @@ func TestDeposit(t *testing.T) { require.Equal(t, expectedBalance.Amount.Sub(depositTokens), acc.GetCoins().AmountOf(sdk.DefaultBondDenom)) // query tx - txs := getTransactions(t, port, fmt.Sprintf("action=deposit&depositor=%s", addr)) + txs := getTransactions(t, port, fmt.Sprintf("action=deposit&sender=%s", addr)) require.Len(t, txs, 1) require.Equal(t, resultTx.Height, txs[0].Height) @@ -735,7 +736,7 @@ func TestVote(t *testing.T) { expectedBalance = coins[0] // query tx - txs := getTransactions(t, port, fmt.Sprintf("action=vote&voter=%s", addr)) + txs := getTransactions(t, port, fmt.Sprintf("action=vote&sender=%s", addr)) require.Len(t, txs, 1) require.Equal(t, resultTx.Height, txs[0].Height) @@ -1004,9 +1005,10 @@ func TestDistributionFlow(t *testing.T) { require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) // Query delegator's rewards total + var delRewards disttypes.QueryDelegatorTotalRewardsResponse res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards", operAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.NoError(t, json.Unmarshal([]byte(body), &delRewards)) // Query delegator's withdrawal address var withdrawAddr string @@ -1045,3 +1047,26 @@ func TestMintingQueries(t *testing.T) { var annualProvisions sdk.Dec require.NoError(t, cdc.UnmarshalJSON([]byte(body), &annualProvisions)) } + +func TestAccountBalanceQuery(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() + + bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6") + require.NoError(t, err) + someFakeAddr := sdk.AccAddress(bz) + + // empty account + res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", someFakeAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.Contains(t, body, `"type":"auth/Account"`) + + // empty account balance + res, body = Request(t, port, "GET", fmt.Sprintf("/bank/balances/%s", someFakeAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.Contains(t, body, "[]") + +} diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 11deeab656..c099c6f122 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -236,7 +236,7 @@ paths: - in: query name: tag 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'" + description: "transaction tags such as 'action=submit-proposal' and 'sender=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc' which results in the following endpoint: 'GET /txs?action=submit-proposal&sender=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'" required: true x-example: 'TODO' - in: query @@ -1486,9 +1486,7 @@ paths: 200: description: OK schema: - type: array - items: - $ref: "#/definitions/Coin" + $ref: "#/definitions/DelegatorTotalRewards" 400: description: Invalid delegator address 500: @@ -2112,6 +2110,26 @@ definitions: $ref: "#/definitions/BlockID" block: $ref: "#/definitions/Block" + DelegationDelegatorReward: + type: object + properties: + validator_address: + $ref: "#/definitions/ValidatorAddress" + reward: + type: array + items: + $ref: "#/definitions/Coin" + DelegatorTotalRewards: + type: object + properties: + rewards: + type: array + items: + $ref: "#/definitions/DelegationDelegatorReward" + total: + type: array + items: + $ref: "#/definitions/Coin" BaseReq: type: object properties: diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 9cd87362cf..329b93ff69 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -695,7 +695,7 @@ func doTransferWithGas( kb := crkeys.NewInMemory() receiveInfo, _, err := kb.CreateMnemonic( - "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), + "receive_address", crkeys.English, client.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), ) require.Nil(t, err) @@ -742,7 +742,7 @@ func doTransferWithGasAccAuto( acc := getAccount(t, port, addr) receiveInfo, _, err := kb.CreateMnemonic( - "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), + "receive_address", crkeys.English, client.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), ) require.Nil(t, err) diff --git a/client/tx/utils.go b/client/tx/utils.go index 41dfd72e88..b15ac43f86 100644 --- a/client/tx/utils.go +++ b/client/tx/utils.go @@ -6,11 +6,12 @@ import ( "strings" "time" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - ctypes "github.com/tendermint/tendermint/rpc/core/types" ) // SearchTxs performs a search for transactions for a given set of tags via diff --git a/cmd/gaia/Makefile b/cmd/gaia/Makefile new file mode 100644 index 0000000000..f966ee4361 --- /dev/null +++ b/cmd/gaia/Makefile @@ -0,0 +1,154 @@ +#!/usr/bin/make -f + +PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') +COMMIT := $(shell git log -1 --format='%H') +LEDGER_ENABLED ?= true +GOBIN ?= $(GOPATH)/bin +GOSUM := $(shell which gosum) + +export GO111MODULE = on + +# process build tags + +build_tags = netgo +ifeq ($(LEDGER_ENABLED),true) + ifeq ($(OS),Windows_NT) + GCCEXE = $(shell where gcc.exe 2> NUL) + ifeq ($(GCCEXE),) + $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S),OpenBSD) + $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) + else + GCC = $(shell command -v gcc 2> /dev/null) + ifeq ($(GCC),) + $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + endif + endif +endif + +ifeq ($(WITH_CLEVELDB),yes) + build_tags += gcc +endif +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) + +# process linker flags + +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.BuildTags=$(build_tags)" + +ifneq ($(GOSUM),) +ldflags += -X github.com/cosmos/cosmos-sdk/version.GoSumHash=$(shell $(GOSUM) ../../go.sum) +endif + +ifeq ($(WITH_CLEVELDB),yes) + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb +endif +ldflags += $(LDFLAGS) +ldflags := $(strip $(ldflags)) + +BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' + +# The below include contains the tools target. +include ../../contrib/devtools/Makefile + +all: install lint check + +build: ../../go.sum +ifeq ($(OS),Windows_NT) + 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 -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: ../../go.sum + LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build + +install: ../../go.sum check-ledger + 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.sum + go install -mod=readonly $(BUILD_FLAGS) ../../cmd/gaia/cmd/gaiadebug + + +######################################## +### Tools & dependencies + +go-mod-cache: go.sum + @echo "--> Download go modules to local cache" + @go mod download + +go.sum: go.mod + @echo "--> Ensure dependencies have not been modified" + @go mod verify + +draw-deps: + @# requires brew install graphviz or apt-get install graphviz + go get github.com/RobotsAndPencils/goviz + @goviz -i ./cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png + +clean: + rm -rf snapcraft-local.yaml build/ + +distclean: clean + rm -rf vendor/ + +######################################## +### Testing + + +check: check-unit +check-unit: + @VERSION=$(VERSION) go test -mod=readonly -race -tags='ledger test_ledger_mock' ./... + +check-race: + @VERSION=$(VERSION) go test -mod=readonly -tags='ledger test_ledger_mock' -race ./... + +check-cover: + @go test -mod=readonly -timeout 30m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' ./... + +check-build: build + @go test -mod=readonly -p 4 `go list ./cli_test/...` -tags=cli_test + +check-all: check-unit check-race check-cover check-build + +lint: 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 + go mod verify + +format: + 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 -mod=readonly -bench=. ./... + + +# include simulations +include sims.mk + +.PHONY: all build-linux install install-debug \ + go-mod-cache draw-deps clean \ + check check-all check-build check-cover check-ledger check-unit check-race + diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index d257627b29..26d920d5ea 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -27,8 +27,6 @@ import ( const ( appName = "GaiaApp" - // DefaultKeyPass contains the default key password for genesis transactions - DefaultKeyPass = "12345678" ) // default home directories for expected binaries diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 80a268ee28..f8ebb62c0a 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "io" "io/ioutil" "math/rand" "os" @@ -61,11 +62,11 @@ func init() { } // helper function for populating input for SimulateFromSeed -func getSimulateFromSeedInput(tb testing.TB, app *GaiaApp) ( - testing.TB, *baseapp.BaseApp, simulation.AppStateFn, int64, +func getSimulateFromSeedInput(tb testing.TB, w io.Writer, app *GaiaApp) ( + testing.TB, io.Writer, *baseapp.BaseApp, simulation.AppStateFn, int64, simulation.WeightedOperations, sdk.Invariants, int, int, bool, bool) { - return tb, app.BaseApp, appStateFn, seed, + return tb, w, app.BaseApp, appStateFn, seed, testAndRunTxs(app), invariants(app), numBlocks, blockSize, commit, lean } @@ -119,7 +120,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest if int64(i) > numInitiallyBonded && r.Intn(100) < 50 { var ( vacc auth.VestingAccount - endTime int + endTime int64 ) startTime := genesisTimestamp.Unix() @@ -127,15 +128,19 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest // Allow for some vesting accounts to vest very quickly while others very // slowly. if r.Intn(100) < 50 { - endTime = randIntBetween(r, int(startTime), int(startTime+(60*60*24*30))) + endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*24*30)))) } else { - endTime = randIntBetween(r, int(startTime), int(startTime+(60*60*12))) + endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*12)))) + } + + if startTime == endTime { + endTime += 1 } if r.Intn(100) < 50 { - vacc = auth.NewContinuousVestingAccount(&bacc, startTime, int64(endTime)) + vacc = auth.NewContinuousVestingAccount(&bacc, startTime, endTime) } else { - vacc = auth.NewDelayedVestingAccount(&bacc, int64(endTime)) + vacc = auth.NewDelayedVestingAccount(&bacc, endTime) } gacc = NewGenesisAccountI(vacc) @@ -148,11 +153,11 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest authGenesis := auth.GenesisState{ Params: auth.Params{ - MaxMemoCharacters: uint64(randIntBetween(r, 100, 200)), + MaxMemoCharacters: uint64(simulation.RandIntBetween(r, 100, 200)), TxSigLimit: uint64(r.Intn(7) + 1), - TxSizeCostPerByte: uint64(randIntBetween(r, 5, 15)), - SigVerifyCostED25519: uint64(randIntBetween(r, 500, 1000)), - SigVerifyCostSecp256k1: uint64(randIntBetween(r, 500, 1000)), + TxSizeCostPerByte: uint64(simulation.RandIntBetween(r, 5, 15)), + SigVerifyCostED25519: uint64(simulation.RandIntBetween(r, 500, 1000)), + SigVerifyCostSecp256k1: uint64(simulation.RandIntBetween(r, 500, 1000)), }, } fmt.Printf("Selected randomly generated auth parameters:\n\t%+v\n", authGenesis) @@ -182,7 +187,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest stakingGenesis := staking.GenesisState{ Pool: staking.InitialPool(), Params: staking.Params{ - UnbondingTime: time.Duration(randIntBetween(r, 60, 60*60*24*3*2)) * time.Second, + UnbondingTime: time.Duration(simulation.RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second, MaxValidators: uint16(r.Intn(250) + 1), BondDenom: sdk.DefaultBondDenom, }, @@ -192,9 +197,9 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest slashingGenesis := slashing.GenesisState{ Params: slashing.Params{ MaxEvidenceAge: stakingGenesis.Params.UnbondingTime, - SignedBlocksWindow: int64(randIntBetween(r, 10, 1000)), + SignedBlocksWindow: int64(simulation.RandIntBetween(r, 10, 1000)), MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1), - DowntimeJailDuration: time.Duration(randIntBetween(r, 60, 60*60*24)) * time.Second, + DowntimeJailDuration: time.Duration(simulation.RandIntBetween(r, 60, 60*60*24)) * time.Second, SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))), SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))), }, @@ -269,10 +274,6 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T return appStateRandomizedFn(r, accs, genesisTimestamp) } -func randIntBetween(r *rand.Rand, min, max int) int { - return r.Intn(max-min) + min -} - func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ {5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)}, @@ -293,12 +294,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { } func invariants(app *GaiaApp) []sdk.Invariant { - return []sdk.Invariant{ - 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), - } + return simulation.PeriodicInvariants(app.crisisKeeper.Invariants(), period, 0) } // Pass this in as an option to use a dbStoreAdapter instead of an IAVLStore for simulation speed. @@ -323,7 +319,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { // Run randomized simulation // TODO parameterize numbers, save for a later PR - _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(b, app)) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(b, os.Stdout, app)) if err != nil { fmt.Println(err) b.Fail() @@ -358,7 +354,7 @@ func TestFullGaiaSimulation(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) if commit { // for memdb: // fmt.Println("Database Size", db.Stats()["database.size"]) @@ -392,7 +388,7 @@ func TestGaiaImportExport(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) + _, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) if commit { // for memdb: @@ -482,7 +478,7 @@ func TestGaiaSimulationAfterImport(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - stopEarly, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app)) + stopEarly, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) if commit { // for memdb: @@ -521,7 +517,7 @@ func TestGaiaSimulationAfterImport(t *testing.T) { }) // Run randomized simulation on imported app - _, err = simulation.SimulateFromSeed(getSimulateFromSeedInput(t, newApp)) + _, err = simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, newApp)) require.Nil(t, err) } @@ -546,7 +542,7 @@ func TestAppStateDeterminism(t *testing.T) { // Run randomized simulation simulation.SimulateFromSeed( - t, app.BaseApp, appStateFn, seed, + t, os.Stdout, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []sdk.Invariant{}, 50, @@ -562,3 +558,42 @@ func TestAppStateDeterminism(t *testing.T) { } } } + +func BenchmarkInvariants(b *testing.B) { + // 1. Setup a simulated Gaia application + logger := log.NewNopLogger() + dir, _ := ioutil.TempDir("", "goleveldb-gaia-invariant-bench") + db, _ := sdk.NewLevelDB("simulation", dir) + + defer func() { + db.Close() + os.RemoveAll(dir) + }() + + app := NewGaiaApp(logger, db, nil, true, 0) + + // 2. Run parameterized simulation (w/o invariants) + _, err := simulation.SimulateFromSeed( + b, ioutil.Discard, app.BaseApp, appStateFn, seed, testAndRunTxs(app), + []sdk.Invariant{}, numBlocks, blockSize, commit, lean, + ) + if err != nil { + fmt.Println(err) + b.FailNow() + } + + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1}) + + // 3. Benchmark each invariant separately + // + // NOTE: We use the crisis keeper as it has all the invariants registered with + // their respective metadata which makes it useful for testing/benchmarking. + for _, cr := range app.crisisKeeper.Routes() { + b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) { + if err := cr.Invar(ctx); err != nil { + fmt.Println(err) + b.FailNow() + } + }) + } +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index b71d6c1c2c..2cb0d22239 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/tendermint/tendermint/crypto/ed25519" + tmtypes "github.com/tendermint/tendermint/types" "github.com/stretchr/testify/require" @@ -223,6 +224,17 @@ func TestGaiaCLISend(t *testing.T) { success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--dry-run") require.True(t, success) + // Test --generate-only + success, stdout, stderr := f.TxSend( + fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only=true", + ) + require.Empty(t, stderr) + require.True(t, success) + msg := unmarshalStdTx(f.T, stdout) + require.NotZero(t, msg.Fee.Gas) + require.Len(t, msg.Msgs, 1) + require.Len(t, msg.GetSignatures(), 0) + // Check state didn't change fooAcc = f.QueryAccount(fooAddr) require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) @@ -415,6 +427,33 @@ func TestGaiaCLICreateValidator(t *testing.T) { f.Cleanup() } +func TestGaiaCLIQueryRewards(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + genesisState := f.GenesisState() + inflationMin := sdk.MustNewDecFromStr("10000.0") + genesisState.MintData.Minter.Inflation = inflationMin + genesisState.MintData.Params.InflationMin = inflationMin + genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") + genFile := filepath.Join(f.GaiadHome, "config", "genesis.json") + genDoc, err := tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + cdc := app.MakeCodec() + genDoc.AppState, err = cdc.MarshalJSON(genesisState) + require.NoError(t, genDoc.SaveAs(genFile)) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + rewards := f.QueryRewards(fooAddr) + require.Equal(t, 1, len(rewards.Rewards)) + + f.Cleanup() +} + func TestGaiaCLISubmitProposal(t *testing.T) { t.Parallel() f := InitFixtures(t) @@ -456,7 +495,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { tests.WaitForNextNBlocksTM(1, f.Port) // Ensure transaction tags can be queried - txs := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("proposer:%s", fooAddr)) + txs := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr)) require.Len(t, txs, 1) // Ensure deposit was deducted @@ -500,7 +539,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, proposalTokens.Add(depositTokens), deposit.Amount.AmountOf(denom)) // Ensure tags are set on the transaction - txs = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("depositor:%s", fooAddr)) + txs = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("sender:%s", fooAddr)) require.Len(t, txs, 1) // Ensure account has expected amount of funds @@ -537,7 +576,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, gov.OptionYes, votes[0].Option) // Ensure tags are applied to voting transaction properly - txs = f.QueryTxs(1, 50, "action:vote", fmt.Sprintf("voter:%s", fooAddr)) + txs = f.QueryTxs(1, 50, "action:vote", fmt.Sprintf("sender:%s", fooAddr)) require.Len(t, txs, 1) // Ensure no proposals in deposit period @@ -975,7 +1014,7 @@ func TestGaiaCLIConfig(t *testing.T) { f.CLIConfig("trace", "false") f.CLIConfig("indent", "true") - config, err := ioutil.ReadFile(path.Join(f.GCLIHome, "config", "config.toml")) + config, err := ioutil.ReadFile(path.Join(f.GaiacliHome, "config", "config.toml")) require.NoError(t, err) expectedConfig := fmt.Sprintf(`broadcast-mode = "block" chain-id = "%s" @@ -992,6 +1031,7 @@ trust-node = true func TestGaiadCollectGentxs(t *testing.T) { t.Parallel() + var customMaxBytes, customMaxGas int64 = 99999999, 1234567 f := NewFixtures(t) // Initialise temporary directories @@ -1011,6 +1051,15 @@ func TestGaiadCollectGentxs(t *testing.T) { // Run init f.GDInit(keyFoo) + // Customise genesis.json + + genFile := f.GenesisFile() + genDoc, err := tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + genDoc.ConsensusParams.Block.MaxBytes = customMaxBytes + genDoc.ConsensusParams.Block.MaxGas = customMaxGas + genDoc.SaveAs(genFile) + // Add account to genesis.json f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) @@ -1020,6 +1069,11 @@ func TestGaiadCollectGentxs(t *testing.T) { // Collect gentxs from a custom directory f.CollectGenTxs(fmt.Sprintf("--gentx-dir=%s", gentxDir)) + genDoc, err = tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + require.Equal(t, genDoc.ConsensusParams.Block.MaxBytes, customMaxBytes) + require.Equal(t, genDoc.ConsensusParams.Block.MaxGas, customMaxGas) + f.Cleanup(gentxDir) } diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index aac07abb27..482cc60ad1 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -10,19 +10,22 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/client" + "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -58,13 +61,16 @@ var ( // Fixtures is used to setup the testing environment type Fixtures struct { - ChainID string - RPCAddr string - Port string - GDHome string - GCLIHome string - P2PAddr string - T *testing.T + BuildDir string + GaiadBinary string + GaiacliBinary string + ChainID string + RPCAddr string + Port string + GaiadHome string + GaiacliHome string + P2PAddr string + T *testing.T } // NewFixtures creates a new instance of Fixtures with many vars set @@ -78,25 +84,34 @@ func NewFixtures(t *testing.T) *Fixtures { p2pAddr, _, err := server.FreeTCPAddr() require.NoError(t, err) + buildDir := os.Getenv("BUILDDIR") + if buildDir == "" { + buildDir, err = filepath.Abs("../../../build/") + require.NoError(t, err) + } + return &Fixtures{ - T: t, - GDHome: filepath.Join(tmpDir, ".gaiad"), - GCLIHome: filepath.Join(tmpDir, ".gaiacli"), - RPCAddr: servAddr, - P2PAddr: p2pAddr, - Port: port, + T: t, + BuildDir: buildDir, + GaiadBinary: filepath.Join(buildDir, "gaiad"), + GaiacliBinary: filepath.Join(buildDir, "gaiacli"), + GaiadHome: filepath.Join(tmpDir, ".gaiad"), + GaiacliHome: filepath.Join(tmpDir, ".gaiacli"), + RPCAddr: servAddr, + P2PAddr: p2pAddr, + Port: port, } } // GenesisFile returns the path of the genesis file func (f Fixtures) GenesisFile() string { - return filepath.Join(f.GDHome, "config", "genesis.json") + return filepath.Join(f.GaiadHome, "config", "genesis.json") } // GenesisFile returns the application's genesis state func (f Fixtures) GenesisState() app.GenesisState { cdc := codec.New() - genDoc, err := appInit.LoadGenesisDoc(cdc, f.GenesisFile()) + genDoc, err := tmtypes.GenesisDocFromFile(f.GenesisFile()) require.NoError(f.T, err) var appState app.GenesisState @@ -150,7 +165,7 @@ func InitFixtures(t *testing.T) (f *Fixtures) { // Cleanup is meant to be run at the end of a test to clean up an remaining test state func (f *Fixtures) Cleanup(dirs ...string) { - clean := append(dirs, f.GDHome, f.GCLIHome) + clean := append(dirs, f.GaiadHome, f.GaiacliHome) for _, d := range clean { err := os.RemoveAll(d) require.NoError(f.T, err) @@ -159,7 +174,7 @@ func (f *Fixtures) Cleanup(dirs ...string) { // Flags returns the flags necessary for making most CLI calls func (f *Fixtures) Flags() string { - return fmt.Sprintf("--home=%s --node=%s", f.GCLIHome, f.RPCAddr) + return fmt.Sprintf("--home=%s --node=%s", f.GaiacliHome, f.RPCAddr) } //___________________________________________________________________________________ @@ -167,17 +182,17 @@ func (f *Fixtures) Flags() string { // UnsafeResetAll is gaiad unsafe-reset-all func (f *Fixtures) UnsafeResetAll(flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad --home=%s unsafe-reset-all", f.GDHome) + cmd := fmt.Sprintf("%s --home=%s unsafe-reset-all", f.GaiadBinary, f.GaiadHome) executeWrite(f.T, addFlags(cmd, flags)) - err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx")) + err := os.RemoveAll(filepath.Join(f.GaiadHome, "config", "gentx")) require.NoError(f.T, err) } // GDInit is gaiad init // NOTE: GDInit sets the ChainID for the Fixtures instance func (f *Fixtures) GDInit(moniker string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad init -o --home=%s %s", f.GDHome, moniker) - _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s init -o --home=%s %s", f.GaiadBinary, f.GaiadHome, moniker) + _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), client.DefaultKeyPass) var chainID string var initRes map[string]json.RawMessage @@ -193,25 +208,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("../../../build/gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome) + cmd := fmt.Sprintf("%s add-genesis-account %s %s --home=%s", f.GaiadBinary, address, coins, f.GaiadHome) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } // GenTx is gaiad gentx func (f *Fixtures) GenTx(name string, flags ...string) { - 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) + cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s", f.GaiadBinary, name, f.GaiadHome, f.GaiacliHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // CollectGenTxs is gaiad collect-gentxs func (f *Fixtures) CollectGenTxs(flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad collect-gentxs --home=%s", f.GDHome) - executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.GaiadBinary, f.GaiadHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // GDStart runs gaiad start with the appropriate flags and returns a process func (f *Fixtures) GDStart(flags ...string) *tests.Process { - cmd := fmt.Sprintf("../../../build/gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr) + cmd := fmt.Sprintf("%s start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GaiadBinary, f.GaiadHome, f.RPCAddr, f.P2PAddr) proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) tests.WaitForTMStart(f.Port) tests.WaitForNextNBlocksTM(1, f.Port) @@ -220,7 +235,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("../../../build/gaiad tendermint %s --home=%s", query, f.GDHome) + cmd := fmt.Sprintf("%s tendermint %s --home=%s", f.GaiadBinary, query, f.GaiadHome) success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) require.Empty(f.T, stderr) require.True(f.T, success) @@ -229,7 +244,7 @@ func (f *Fixtures) GDTendermint(query string) string { // ValidateGenesis runs gaiad validate-genesis func (f *Fixtures) ValidateGenesis() { - cmd := fmt.Sprintf("../../../build/gaiad validate-genesis --home=%s", f.GDHome) + cmd := fmt.Sprintf("%s validate-genesis --home=%s", f.GaiadBinary, f.GaiadHome) executeWriteCheckErr(f.T, cmd) } @@ -238,31 +253,31 @@ func (f *Fixtures) ValidateGenesis() { // KeysDelete is gaiacli keys delete func (f *Fixtures) KeysDelete(name string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys delete --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys delete --home=%s %s", f.GaiacliBinary, f.GaiacliHome, 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("../../../build/gaiacli keys add --home=%s %s", f.GCLIHome, name) - executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s keys add --home=%s %s", f.GaiacliBinary, f.GaiacliHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // KeysAddRecover prepares gaiacli keys add --recover func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) - executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s", f.GaiacliBinary, f.GaiacliHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.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("../../../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) + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s --account %d --index %d", f.GaiacliBinary, f.GaiacliHome, name, account, index) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic) } // KeysShow is gaiacli keys show func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { - cmd := fmt.Sprintf("../../../build/gaiacli keys show --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys show --home=%s %s", f.GaiacliBinary, f.GaiacliHome, name) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ko keys.KeyOutput err := clientkeys.UnmarshalJSON([]byte(out), &ko) @@ -283,7 +298,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("../../../build/gaiacli config --home=%s %s %s", f.GCLIHome, key, value) + cmd := fmt.Sprintf("%s config --home=%s %s %s", f.GaiacliBinary, f.GaiacliHome, key, value) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } @@ -292,41 +307,41 @@ 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("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx send %s %s %s %v", f.GaiacliBinary, from, to, amount, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } func (f *Fixtures) txSendWithConfirm( from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string, ) (bool, string, string) { - 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) + cmd := fmt.Sprintf("%s tx send %s %s %s %v", f.GaiacliBinary, from, to, amount, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, client.DefaultKeyPass) } // TxSign is gaiacli tx sign func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx sign %v --from=%s %v", f.GaiacliBinary, f.Flags(), signer, fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // TxBroadcast is gaiacli tx broadcast func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx broadcast %v %v", f.Flags(), fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx broadcast %v %v", f.GaiacliBinary, f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // TxEncode is gaiacli tx encode func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx encode %v %v", f.Flags(), fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx encode %v %v", f.GaiacliBinary, f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // TxMultisign is gaiacli tx multisign func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx multisign %v %s %s %s", f.Flags(), + cmd := fmt.Sprintf("%s tx multisign %v %s %s %s", f.GaiacliBinary, f.Flags(), fileName, name, strings.Join(signaturesFiles, " "), ) return executeWriteRetStdStreams(f.T, cmd) @@ -337,17 +352,17 @@ 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("../../../build/gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey) + cmd := fmt.Sprintf("%s tx staking create-validator %v --from=%s --pubkey=%s", f.GaiacliBinary, 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") - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } // TxStakingUnbond is gaiacli tx staking unbond func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { - 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) + cmd := fmt.Sprintf("%s tx staking unbond %s %v --from=%s %v", f.GaiacliBinary, validator, shares, from, f.Flags()) + return executeWrite(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } //___________________________________________________________________________________ @@ -355,21 +370,21 @@ 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("../../../build/gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ) + cmd := fmt.Sprintf("%s tx gov submit-proposal %v --from=%s --type=%s", f.GaiacliBinary, 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) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.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("../../../build/gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags()) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx gov deposit %d %s --from=%s %v", f.GaiacliBinary, proposalID, amount, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.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("../../../build/gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags()) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + cmd := fmt.Sprintf("%s tx gov vote %d %s --from=%s %v", f.GaiacliBinary, proposalID, option, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } //___________________________________________________________________________________ @@ -377,7 +392,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("../../../build/gaiacli query account %s %v", address, f.Flags()) + cmd := fmt.Sprintf("%s query account %s %v", f.GaiacliBinary, address, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var initRes map[string]json.RawMessage err := json.Unmarshal([]byte(out), &initRes) @@ -396,7 +411,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("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.GaiacliBinary, page, limit, queryTags(tags), f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var txs []sdk.TxResponse cdc := app.MakeCodec() @@ -407,7 +422,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("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.GaiacliBinary, page, limit, queryTags(tags), f.Flags()) _, err := tests.ExecuteT(f.T, cmd, "") require.EqualError(f.T, expectedErr, err) } @@ -417,7 +432,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("../../../build/gaiacli query staking validator %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking validator %s %v", f.GaiacliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var validator staking.Validator cdc := app.MakeCodec() @@ -428,7 +443,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("../../../build/gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking unbonding-delegations-from %s %v", f.GaiacliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ubds []staking.UnbondingDelegation cdc := app.MakeCodec() @@ -439,7 +454,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("../../../build/gaiacli query staking delegations-to %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking delegations-to %s %v", f.GaiacliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var delegations []staking.Delegation cdc := app.MakeCodec() @@ -450,7 +465,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("../../../build/gaiacli query staking pool %v", f.Flags()) + cmd := fmt.Sprintf("%s query staking pool %v", f.GaiacliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var pool staking.Pool cdc := app.MakeCodec() @@ -461,7 +476,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("../../../build/gaiacli query staking params %v", f.Flags()) + cmd := fmt.Sprintf("%s query staking params %v", f.GaiacliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var params staking.Params cdc := app.MakeCodec() @@ -475,7 +490,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("../../../build/gaiacli query gov param deposit %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param deposit %s", f.GaiacliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var depositParam gov.DepositParams cdc := app.MakeCodec() @@ -486,7 +501,7 @@ func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { // QueryGovParamVoting is gaiacli query gov param voting func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { - cmd := fmt.Sprintf("../../../build/gaiacli query gov param voting %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param voting %s", f.GaiacliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var votingParam gov.VotingParams cdc := app.MakeCodec() @@ -497,7 +512,7 @@ func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { // QueryGovParamTallying is gaiacli query gov param tallying func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { - cmd := fmt.Sprintf("../../../build/gaiacli query gov param tallying %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param tallying %s", f.GaiacliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var tallyingParam gov.TallyParams cdc := app.MakeCodec() @@ -508,7 +523,7 @@ func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { // QueryGovProposals is gaiacli query gov proposals func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { - cmd := fmt.Sprintf("../../../build/gaiacli query gov proposals %v", f.Flags()) + cmd := fmt.Sprintf("%s query gov proposals %v", f.GaiacliBinary, f.Flags()) stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") if strings.Contains(stderr, "No matching proposals found") { return gov.Proposals{} @@ -523,7 +538,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("../../../build/gaiacli query gov proposal %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov proposal %d %v", f.GaiacliBinary, proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var proposal gov.Proposal cdc := app.MakeCodec() @@ -534,7 +549,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("../../../build/gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags()) + cmd := fmt.Sprintf("%s query gov vote %d %s %v", f.GaiacliBinary, proposalID, voter, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var vote gov.Vote cdc := app.MakeCodec() @@ -545,7 +560,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("../../../build/gaiacli query gov votes %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov votes %d %v", f.GaiacliBinary, proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var votes []gov.Vote cdc := app.MakeCodec() @@ -556,7 +571,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("../../../build/gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags()) + cmd := fmt.Sprintf("%s query gov deposit %d %s %v", f.GaiacliBinary, proposalID, depositor, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposit gov.Deposit cdc := app.MakeCodec() @@ -567,7 +582,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("../../../build/gaiacli query gov deposits %d %v", propsalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov deposits %d %v", f.GaiacliBinary, propsalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposits []gov.Deposit cdc := app.MakeCodec() @@ -581,7 +596,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("../../../build/gaiacli query slashing signing-info %s %s", val, f.Flags()) + cmd := fmt.Sprintf("%s query slashing signing-info %s %s", f.GaiacliBinary, val, f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() @@ -593,7 +608,7 @@ func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { // QuerySlashingParams is gaiacli query slashing params func (f *Fixtures) QuerySlashingParams() slashing.Params { - cmd := fmt.Sprintf("../../../build/gaiacli query slashing params %s", f.Flags()) + cmd := fmt.Sprintf("%s query slashing params %s", f.GaiacliBinary, f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() @@ -603,6 +618,21 @@ func (f *Fixtures) QuerySlashingParams() slashing.Params { return params } +//___________________________________________________________________________________ +// query distribution + +// QuerySigningInfo returns the signing info for a validator +func (f *Fixtures) QueryRewards(delAddr sdk.AccAddress, flags ...string) distribution.QueryDelegatorTotalRewardsResponse { + cmd := fmt.Sprintf("%s query distr rewards %s %s", f.GaiacliBinary, delAddr, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var rewards distribution.QueryDelegatorTotalRewardsResponse + err := cdc.UnmarshalJSON([]byte(res), &rewards) + require.NoError(f.T, err) + return rewards +} + //___________________________________________________________________________________ // executors diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 58e6cf5761..5088241b50 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -70,6 +70,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application logger, db, traceStore, true, invCheckPeriod, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), + baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))), ) } diff --git a/cmd/gaia/cmd/gaiakeyutil/main.go b/cmd/gaia/cmd/gaiakeyutil/main.go index 46eefc69cc..30aebed3bf 100644 --- a/cmd/gaia/cmd/gaiakeyutil/main.go +++ b/cmd/gaia/cmd/gaiakeyutil/main.go @@ -5,8 +5,9 @@ import ( "fmt" "os" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/libs/bech32" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var bech32Prefixes = []string{ diff --git a/cmd/gaia/contrib/runsim/main.go b/cmd/gaia/contrib/runsim/main.go new file mode 100644 index 0000000000..2d13719f9a --- /dev/null +++ b/cmd/gaia/contrib/runsim/main.go @@ -0,0 +1,228 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "os/signal" + "path/filepath" + "strings" + "sync" + "syscall" + "time" +) + +var ( + // default seeds + seeds = []int{ + 1, 2, 4, 7, 32, 123, 124, 582, 1893, 2989, + 3012, 4728, 37827, 981928, 87821, 891823782, + 989182, 89182391, 11, 22, 44, 77, 99, 2020, + 3232, 123123, 124124, 582582, 18931893, + 29892989, 30123012, 47284728, 37827, + } + + // goroutine-safe process map + procs map[int]*os.Process + mutex *sync.Mutex + + // results channel + results chan bool + + // command line arguments and options + jobs int + blocks string + period string + testname string + genesis string + + // logs temporary directory + tempdir string +) + +func init() { + log.SetPrefix("") + log.SetFlags(0) + + procs = map[int]*os.Process{} + mutex = &sync.Mutex{} + flag.IntVar(&jobs, "j", 10, "Number of parallel processes") + flag.StringVar(&genesis, "g", "", "Genesis file") + + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), + `Usage: %s [-j maxprocs] [-g genesis.json] [blocks] [period] [testname] +Run simulations in parallel + +`, filepath.Base(os.Args[0])) + flag.PrintDefaults() + } +} + +func main() { + var err error + + flag.Parse() + if flag.NArg() != 3 { + log.Fatal("wrong number of arguments") + } + + // prepare input channel + queue := make(chan int, len(seeds)) + for _, seed := range seeds { + queue <- seed + } + close(queue) + + // jobs cannot be > len(seeds) + if jobs > len(seeds) { + jobs = len(seeds) + } + results = make(chan bool, len(seeds)) + + // setup signal handling + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + _ = <-sigs + fmt.Println() + + // drain the queue + log.Printf("Draining seeds queue...") + for seed := range queue { + log.Printf("%d", seed) + } + log.Printf("Kill all remaining processes...") + killAllProcs() + os.Exit(1) + }() + + // initialise common test parameters + blocks = flag.Arg(0) + period = flag.Arg(1) + testname = flag.Arg(2) + tempdir, err = ioutil.TempDir("", "") + if err != nil { + log.Fatal(err) + } + + // set up worker pool + wg := sync.WaitGroup{} + for workerId := 0; workerId < jobs; workerId++ { + wg.Add(1) + + go func(workerId int) { + defer wg.Done() + worker(workerId, queue) + }(workerId) + } + + // idiomatic hack required to use wg.Wait() with select + waitCh := make(chan struct{}) + go func() { + defer close(waitCh) + wg.Wait() + }() + +wait: + for { + select { + case <-waitCh: + break wait + case <-time.After(1 * time.Minute): + fmt.Println(".") + } + } + + // analyse results and exit with 1 on first error + close(results) + for rc := range results { + if !rc { + os.Exit(1) + } + } + + os.Exit(0) +} + +func buildCommand(testname, blocks, period, genesis string, seed int) string { + return fmt.Sprintf("go test github.com/cosmos/cosmos-sdk/cmd/gaia/app -run %s -SimulationEnabled=true "+ + "-SimulationNumBlocks=%s -SimulationGenesis=%s "+ + "-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=%d -SimulationPeriod=%s -v -timeout 24h", + testname, blocks, genesis, seed, period) +} + +func makeCmd(cmdStr string) *exec.Cmd { + cmdSlice := strings.Split(cmdStr, " ") + return exec.Command(cmdSlice[0], cmdSlice[1:]...) +} + +func makeFilename(seed int) string { + return fmt.Sprintf("gaia-simulation-seed-%d-date-%s", seed, time.Now().Format("01-02-2006_15:04:05.000000000")) +} + +func worker(id int, seeds <-chan int) { + log.Printf("[W%d] Worker is up and running", id) + for seed := range seeds { + if err := spawnProc(id, seed); err != nil { + results <- false + log.Printf("[W%d] Seed %d: FAILED", id, seed) + log.Printf("To reproduce run: %s", buildCommand(testname, blocks, period, genesis, seed)) + } else { + log.Printf("[W%d] Seed %d: OK", id, seed) + } + } + log.Printf("[W%d] no seeds left, shutting down", id) +} + +func spawnProc(workerId int, seed int) error { + stderrFile, _ := os.Create(filepath.Join(tempdir, makeFilename(seed)+".stderr")) + stdoutFile, _ := os.Create(filepath.Join(tempdir, makeFilename(seed)+".stdout")) + s := buildCommand(testname, blocks, period, genesis, seed) + cmd := makeCmd(s) + cmd.Stdout = stdoutFile + cmd.Stderr = stderrFile + err := cmd.Start() + if err != nil { + log.Printf("couldn't start %q", s) + return err + } + log.Printf("[W%d] Spawned simulation with pid %d [seed=%d stdout=%s stderr=%s]", + workerId, cmd.Process.Pid, seed, stdoutFile.Name(), stderrFile.Name()) + pushProcess(cmd.Process) + defer popProcess(cmd.Process) + return cmd.Wait() +} + +func pushProcess(proc *os.Process) { + mutex.Lock() + defer mutex.Unlock() + procs[proc.Pid] = proc +} + +func popProcess(proc *os.Process) { + mutex.Lock() + defer mutex.Unlock() + if _, ok := procs[proc.Pid]; ok { + delete(procs, proc.Pid) + } +} + +func killAllProcs() { + mutex.Lock() + defer mutex.Unlock() + for _, proc := range procs { + checkSignal(proc, syscall.SIGTERM) + checkSignal(proc, syscall.SIGKILL) + } +} + +func checkSignal(proc *os.Process, signal syscall.Signal) { + if err := proc.Signal(signal); err != nil { + log.Printf("Failed to send %s to PID %d", signal, proc.Pid) + } +} diff --git a/scripts/multisim.sh b/cmd/gaia/contrib/sim/multisim.sh similarity index 57% rename from scripts/multisim.sh rename to cmd/gaia/contrib/sim/multisim.sh index 9ff71c5674..09c6f44a5a 100755 --- a/scripts/multisim.sh +++ b/cmd/gaia/contrib/sim/multisim.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + go mod download seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \ @@ -16,33 +18,63 @@ echo "Using genesis file $genesis" echo "Edit scripts/multisim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce." echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)." -trap 'kill $(jobs -pr)' SIGINT SIGTERM +# Symbols prefixes legenda: +# f_ - function +# l_ - local symbols + +f_spinner() { + local l_i l_sp + l_i=1 + l_chars="/-\|" + echo -n ' ' + while true + do + printf "\b${l_chars:l_i++%${#l_chars}:1}" + sleep 1s + done +} + +f_cleanup() { + local l_children + l_children=$(pgrep -P $$) + echo "Stopping children ["${l_children}"] ..." >&2 + kill -SIGSTOP ${l_children} || true + echo "Terminating children ["${l_children}"] ..." >&2 + kill -TERM ${l_children} || true + exit 0 +} + +trap f_cleanup SIGINT SIGTERM EXIT tmpdir=$(mktemp -d) echo "Using temporary log directory: $tmpdir" -sim() { - seed=$1 - echo "Running Gaia simulation with seed $seed. This may take awhile!" - file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout" +f_sim() { + local l_seed + l_seed=$1 + echo "Running Gaia simulation with seed $l_seed. This may take awhile!" + file="$tmpdir/gaia-simulation-seed-$l_seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout" echo "Writing stdout to $file..." - go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks -SimulationGenesis=$genesis \ - -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -SimulationPeriod=$period -v -timeout 24h > $file + go test github.com/cosmos/cosmos-sdk/cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks -SimulationGenesis=$genesis \ + -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$l_seed -SimulationPeriod=$period -v -timeout 24h > $file && \ + echo "Simulation with seed $l_seed OK" || ( code=1 ; echo "Simulation with seed $seed failed!" ) # > $file } +echo "Simulation processes spawned, waiting for completion..." + +f_spinner & + + i=0 pids=() for seed in ${seeds[@]}; do - sim $seed & + f_sim $seed & pids[${i}]=$! i=$(($i+1)) sleep 10 # start in order, nicer logs done -echo "Simulation processes spawned, waiting for completion..." - code=0 - i=0 for pid in ${pids[*]}; do wait $pid diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index 8586394868..0ad58252d9 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -46,7 +46,7 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return err } - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) if err != nil { return err } @@ -59,7 +59,7 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) initCfg := newInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey) - appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + appMessage, err := genAppStateFromConfig(cdc, config, initCfg, *genDoc) if err != nil { return err } @@ -82,7 +82,6 @@ func genAppStateFromConfig( cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc, ) (appState json.RawMessage, err error) { - genFile := config.GenesisFile() var ( appGenTxs []auth.StdTx persistentPeers string @@ -116,7 +115,8 @@ func genAppStateFromConfig( return } - err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) + genDoc.AppState = appState + err = ExportGenesisFile(&genDoc, config.GenesisFile()) return } diff --git a/cmd/gaia/init/genesis_accts.go b/cmd/gaia/init/genesis_accts.go index 5aa73dae94..79748f1ea0 100644 --- a/cmd/gaia/init/genesis_accts.go +++ b/cmd/gaia/init/genesis_accts.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -58,7 +59,7 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command return fmt.Errorf("%s does not exist, run `gaiad init` first", genFile) } - genDoc, err := LoadGenesisDoc(cdc, genFile) + genDoc, err := tmtypes.GenesisDocFromFile(genFile) if err != nil { return err } @@ -78,7 +79,8 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command return err } - return ExportGenesisFile(genFile, genDoc.ChainID, nil, appStateJSON) + genDoc.AppState = appStateJSON + return ExportGenesisFile(genDoc, genFile) }, } diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 970c77e0d8..b873cd88b5 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -17,6 +17,7 @@ import ( "github.com/tendermint/tendermint/crypto" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -79,7 +80,7 @@ following delegation and commission default parameters: "the tx's memo field will be unset") } - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) if err != nil { return err } diff --git a/cmd/gaia/init/gentx_test.go b/cmd/gaia/init/gentx_test.go index d99a562fc0..b83088b140 100644 --- a/cmd/gaia/init/gentx_test.go +++ b/cmd/gaia/init/gentx_test.go @@ -3,15 +3,16 @@ 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" + + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" ) func Test_prepareFlagsForTxCreateValidator(t *testing.T) { diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 506557d3f6..b539c86323 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -11,6 +11,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -76,7 +77,22 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { // nolint: return err } - if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil { + genDoc := &types.GenesisDoc{} + if _, err := os.Stat(genFile); err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + genDoc, err = types.GenesisDocFromFile(genFile) + if err != nil { + return err + } + } + + genDoc.ChainID = chainID + genDoc.Validators = nil + genDoc.AppState = appState + if err = ExportGenesisFile(genDoc, genFile); err != nil { return err } diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index 39975bb296..abfc343d17 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -8,16 +8,17 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/mock" "github.com/spf13/viper" "github.com/stretchr/testify/require" abciServer "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/mock" ) func TestInitCmd(t *testing.T) { diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index fc59827a6c..56b0f2f86c 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -157,7 +157,7 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { buf := client.BufferStdin() prompt := fmt.Sprintf( - "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass, + "Password for account '%s' (default %s):", nodeDirName, client.DefaultKeyPass, ) keyPass, err := client.GetPassword(prompt, buf) @@ -169,7 +169,7 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { } if keyPass == "" { - keyPass = app.DefaultKeyPass + keyPass = client.DefaultKeyPass } addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true) @@ -217,7 +217,7 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo) txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb) - signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false) + signedTx, err := txBldr.SignStdTx(nodeDirName, client.DefaultKeyPass, tx, false) if err != nil { _ = os.RemoveAll(outDir) return err @@ -236,6 +236,8 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { return err } + // TODO: Rename config file to server.toml as it's not particular to Gaia + // (REF: https://github.com/cosmos/cosmos-sdk/issues/4125). gaiaConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml") srvconfig.WriteConfigFile(gaiaConfigFilePath, gaiaConfig) } @@ -306,12 +308,12 @@ func collectGenFiles( nodeID, valPubKey := nodeIDs[i], valPubKeys[i] initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey) - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) if err != nil { return err } - nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, *genDoc) if err != nil { return err } diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go index 4e2f2f26f2..ce43781c71 100644 --- a/cmd/gaia/init/utils.go +++ b/cmd/gaia/init/utils.go @@ -3,11 +3,9 @@ package init import ( "encoding/json" "fmt" - "io/ioutil" "path/filepath" "time" - amino "github.com/tendermint/go-amino" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/common" @@ -22,16 +20,7 @@ import ( // ExportGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. -func ExportGenesisFile( - genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, -) error { - - genDoc := types.GenesisDoc{ - ChainID: chainID, - Validators: validators, - AppState: appState, - } - +func ExportGenesisFile(genDoc *types.GenesisDoc, genFile string) error { if err := genDoc.ValidateAndComplete(); err != nil { return err } @@ -88,20 +77,6 @@ func InitializeNodeValidatorFiles( return nodeID, valPubKey, nil } -// LoadGenesisDoc reads and unmarshals GenesisDoc from the given file. -func LoadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { - genContents, err := ioutil.ReadFile(genFile) - if err != nil { - return genDoc, err - } - - if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil { - return genDoc, err - } - - return genDoc, err -} - func initializeEmptyGenesis( cdc *codec.Codec, genFile, chainID string, overwrite bool, ) (appState json.RawMessage, err error) { diff --git a/cmd/gaia/init/utils_test.go b/cmd/gaia/init/utils_test.go index 3a5a136903..87dfabd4d6 100644 --- a/cmd/gaia/init/utils_test.go +++ b/cmd/gaia/init/utils_test.go @@ -2,13 +2,10 @@ package init import ( "encoding/json" - "fmt" - "os" "path/filepath" "testing" "time" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/tests" "github.com/stretchr/testify/require" @@ -22,28 +19,3 @@ func TestExportGenesisFileWithTime(t *testing.T) { fname := filepath.Join(dir, "genesis.json") require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now())) } - -func TestLoadGenesisDoc(t *testing.T) { - t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - - fname := filepath.Join(dir, "genesis.json") - require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now())) - - _, err := LoadGenesisDoc(codec.Cdc, fname) - require.NoError(t, err) - - // Non-existing file - _, err = LoadGenesisDoc(codec.Cdc, "non-existing-file") - require.Error(t, err) - - malformedFilename := filepath.Join(dir, "malformed") - malformedFile, err := os.Create(malformedFilename) - require.NoError(t, err) - fmt.Fprint(malformedFile, "invalidjson") - malformedFile.Close() - // Non-existing file - _, err = LoadGenesisDoc(codec.Cdc, malformedFilename) - require.Error(t, err) -} diff --git a/cmd/gaia/init/validate_genesis.go b/cmd/gaia/init/validate_genesis.go index eb0eaa3a2e..0f7303691e 100644 --- a/cmd/gaia/init/validate_genesis.go +++ b/cmd/gaia/init/validate_genesis.go @@ -4,11 +4,12 @@ import ( "fmt" "os" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" - "github.com/spf13/cobra" - "github.com/tendermint/tendermint/types" ) // Validate genesis command takes @@ -30,8 +31,8 @@ func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { //nolint fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis) - var genDoc types.GenesisDoc - if genDoc, err = LoadGenesisDoc(cdc, genesis); err != nil { + var genDoc *types.GenesisDoc + if genDoc, err = types.GenesisDocFromFile(genesis); err != nil { return fmt.Errorf("Error loading genesis doc from %s: %s", genesis, err.Error()) } diff --git a/cmd/gaia/sims.mk b/cmd/gaia/sims.mk new file mode 100644 index 0000000000..c6776594fc --- /dev/null +++ b/cmd/gaia/sims.mk @@ -0,0 +1,62 @@ +#!/usr/bin/make -f + +######################################## +### Simulations + +runsim: $(GOBIN)/runsim +$(GOBIN)/runsim: contrib/runsim/main.go + go install github.com/cosmos/cosmos-sdk/cmd/gaia/contrib/runsim + +sim-gaia-nondeterminism: + @echo "Running nondeterminism test..." + @go test -mod=readonly ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m + +sim-gaia-custom-genesis-fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + @go test -mod=readonly github.com/cosmos/cosmos-sdk/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 + +sim-gaia-fast: + @echo "Running quick Gaia simulation. This may take several minutes..." + @go test -mod=readonly github.com/cosmos/cosmos-sdk/cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h + +sim-gaia-import-export: runsim + @echo "Running Gaia import/export simulation. This may take several minutes..." + $(GOBIN)/runsim 50 5 TestGaiaImportExport + +sim-gaia-simulation-after-import: runsim + @echo "Running Gaia simulation-after-import. This may take several minutes..." + $(GOBIN)/runsim 50 5 TestGaiaSimulationAfterImport + +sim-gaia-custom-genesis-multi-seed: runsim + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + $(GOBIN)/runsim -g ${HOME}/.gaiad/config/genesis.json 400 5 TestFullGaiaSimulation + +sim-gaia-multi-seed: runsim + @echo "Running multi-seed Gaia simulation. This may take awhile!" + $(GOBIN)/runsim 400 5 TestFullGaiaSimulation + +sim-benchmark-invariants: + @echo "Running simulation invariant benchmarks..." + @go test -mod=readonly github.com/cosmos/cosmos-sdk/cmd/gaia/app -benchmem -bench=BenchmarkInvariants -run=^$ \ + -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationBlockSize=200 \ + -SimulationCommit=true -SimulationSeed=57 -v -timeout 24h + +SIM_NUM_BLOCKS ?= 500 +SIM_BLOCK_SIZE ?= 200 +SIM_COMMIT ?= true +sim-gaia-benchmark: + @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" + @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 + +sim-gaia-profile: + @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" + @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 + +.PHONY: sim-gaia-nondeterminism sim-gaia-custom-genesis-fast sim-gaia-fast sim-gaia-import-export \ + sim-gaia-simulation-after-import sim-gaia-custom-genesis-multi-seed sim-gaia-multi-seed \ + sim-benchmark-invariants sim-gaia-benchmark sim-gaia-profile diff --git a/codec/codec.go b/codec/codec.go index 175eb89c13..21e0b69778 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -5,7 +5,7 @@ import ( "encoding/json" amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/crypto/encoding/amino" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) // amino codec to marshal/unmarshal diff --git a/scripts/Makefile b/contrib/devtools/Makefile similarity index 78% rename from scripts/Makefile rename to contrib/devtools/Makefile index d76720d408..7bee314b51 100644 --- a/scripts/Makefile +++ b/contrib/devtools/Makefile @@ -44,11 +44,11 @@ mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) all: tools tools: tools-stamp -tools-stamp: $(GOBIN)/golangci-lint $(GOBIN)/statik $(GOBIN)/goimports $(GOBIN)/gosum $(GOBIN)/sdkch +tools-stamp: $(GOBIN)/golangci-lint $(GOBIN)/statik $(GOBIN)/goimports $(GOBIN)/gosum $(GOBIN)/clog touch $@ -$(GOBIN)/golangci-lint: contrib/install-golangci-lint.sh $(GOBIN)/gosum - bash contrib/install-golangci-lint.sh $(GOBIN) $(GOLANGCI_LINT_VERSION) $(GOLANGCI_LINT_HASHSUM) +$(GOBIN)/golangci-lint: $(mkfile_dir)/install-golangci-lint.sh $(GOBIN)/gosum + bash $(mkfile_dir)/install-golangci-lint.sh $(GOBIN) $(GOLANGCI_LINT_VERSION) $(GOLANGCI_LINT_HASHSUM) $(GOBIN)/statik: $(call go_install,rakyll,statik,v0.1.5) @@ -57,13 +57,13 @@ $(GOBIN)/goimports: go get golang.org/x/tools/cmd/goimports@v0.0.0-20190114222345-bf090417da8b $(GOBIN)/gosum: - go install -mod=readonly ./cmd/gosum/ + go install -mod=readonly github.com/cosmos/cosmos-sdk/contrib/devtools/gosum/ -$(GOBIN)/sdkch: - go install -mod=readonly ./cmd/sdkch/ +$(GOBIN)/clog: + go install -mod=readonly github.com/cosmos/cosmos-sdk/contrib/devtools/clog/ tools-clean: - cd $(GOBIN) && rm -f golangci-lint statik goimports gosum sdkch + cd $(GOBIN) && rm -f golangci-lint statik goimports gosum clog rm -f tools-stamp .PHONY: all tools tools-clean diff --git a/cmd/sdkch/README.md b/contrib/devtools/clog/README.md similarity index 100% rename from cmd/sdkch/README.md rename to contrib/devtools/clog/README.md diff --git a/cmd/sdkch/main.go b/contrib/devtools/clog/main.go similarity index 50% rename from cmd/sdkch/main.go rename to contrib/devtools/clog/main.go index df3a781453..49107d1cd3 100644 --- a/cmd/sdkch/main.go +++ b/contrib/devtools/clog/main.go @@ -7,12 +7,13 @@ import ( "fmt" "io/ioutil" "log" - "math" "os" "os/exec" "path/filepath" "regexp" "strings" + + "github.com/spf13/cobra" ) const ( @@ -26,8 +27,9 @@ var ( progName string verboseLog *log.Logger - entriesDir string - verboseLogging bool + entriesDir string + verboseLogging bool + interactiveMode bool // sections name-title map sections = map[string]string{ @@ -44,22 +46,91 @@ var ( "sdk": "SDK", "tendermint": "Tendermint", } + + // RootCmd represents the base command when called without any subcommands + RootCmd = &cobra.Command{ + Use: "clog", + Short: "Maintain unreleased changelog entries in a modular fashion.", + } + + // command to add a pending log entry + AddCmd = &cobra.Command{ + Use: "add [section] [stanza] [message]", + Short: "Add an entry file.", + Long: `Add an entry file. If message is empty, start the editor to edit the message. + + Sections Stanzas + --- --- + breaking gaia + features gaiacli +improvements gaiarest + bugfixes sdk + tendermint`, + Args: cobra.MaximumNArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + + if interactiveMode { + return addEntryFileFromConsoleInput() + } + + if len(args) < 2 { + log.Println("must include at least 2 arguments when not in interactive mode") + return nil + } + sectionDir, stanzaDir := args[0], args[1] + err := validateSectionStanzaDirs(sectionDir, stanzaDir) + if err != nil { + return err + } + if len(args) == 3 { + return addSinglelineEntryFile(sectionDir, stanzaDir, strings.TrimSpace(args[2])) + } + return addEntryFile(sectionDir, stanzaDir) + }, + } + + // command to generate the changelog + GenerateCmd = &cobra.Command{ + Use: "generate", + Short: "Generate a changelog in Markdown format and print it to STDOUT. version defaults to UNRELEASED.", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + version := "UNRELEASED" + if flag.NArg() > 1 { + version = strings.Join(flag.Args()[1:], " ") + } + return generateChangelog(version) + }, + } + + // command to delete empty sub-directories recursively + PruneCmd = &cobra.Command{ + Use: "prune", + Short: "Delete empty sub-directories recursively.", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return pruneEmptyDirectories() + }, + } ) func init() { - progName = filepath.Base(os.Args[0]) + RootCmd.AddCommand(AddCmd) + RootCmd.AddCommand(GenerateCmd) + RootCmd.AddCommand(PruneCmd) + 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 - + AddCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "get the section/stanza/message with interactive CLI prompts") + RootCmd.PersistentFlags().BoolVarP(&verboseLogging, "verbose-logging", "v", false, "enable verbose logging") + RootCmd.PersistentFlags().StringVarP(&entriesDir, "entries-dir", "d", filepath.Join(cwd, entriesDirName), "entry files directory") } func main() { + logPrefix := fmt.Sprintf("%s: ", filepath.Base(progName)) log.SetFlags(0) log.SetPrefix(logPrefix) @@ -70,67 +141,65 @@ func main() { 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) + if err := RootCmd.Execute(); err != nil { + log.Fatal(err) } } -func addSinglelineEntryFile(sectionDir, stanzaDir, message string) { +func addEntryFileFromConsoleInput() error { + + reader := bufio.NewReader(os.Stdin) + fmt.Println("Please enter the section (either: \"breaking\", \"features\", \"improvements\", \"bugfixes\")") + sectionDir, _ := reader.ReadString('\n') + sectionDir = strings.TrimSpace(sectionDir) + if _, ok := sections[sectionDir]; !ok { + return errors.New("invalid section, please try again") + } + + fmt.Println("Please enter the stanza (either: \"gaia\", \"gaiacli\", \"gaiarest\", \"sdk\", \"tendermint\")") + stanzaDir, _ := reader.ReadString('\n') + stanzaDir = strings.TrimSpace(stanzaDir) + if _, ok := stanzas[stanzaDir]; !ok { + return errors.New("invalid stanza, please try again") + } + + fmt.Println("Please enter the changelog message (or press enter to write in default $EDITOR)") + message, _ := reader.ReadString('\n') + message = strings.TrimSpace(message) + if message == "" { + return addEntryFile(sectionDir, stanzaDir) + } + + return addSinglelineEntryFile(sectionDir, stanzaDir, message) +} + +func addSinglelineEntryFile(sectionDir, stanzaDir, message string) error { filename := filepath.Join( filepath.Join(entriesDir, sectionDir, stanzaDir), generateFileName(message), ) - writeEntryFile(filename, []byte(message)) + return writeEntryFile(filename, []byte(message)) } -func addEntryFile(sectionDir, stanzaDir string) { - bs := readUserInput() +func addEntryFile(sectionDir, stanzaDir string) error { + bs, err := readUserInputFromEditor() + if err != nil { + return err + } firstLine := strings.TrimSpace(strings.Split(string(bs), "\n")[0]) filename := filepath.Join( filepath.Join(entriesDir, sectionDir, stanzaDir), generateFileName(firstLine), ) - writeEntryFile(filename, bs) + return writeEntryFile(filename, bs) } -var filenameInvalidChars = regexp.MustCompile(`[^a-zA-Z0-9-_]`) - func generateFileName(line string) string { var chunks []string + + filenameInvalidChars := regexp.MustCompile(`[^a-zA-Z0-9-_]`) subsWithInvalidCharsRemoved := strings.Split(filenameInvalidChars.ReplaceAllString(line, " "), " ") for _, sub := range subsWithInvalidCharsRemoved { sub = strings.TrimSpace(sub) @@ -140,17 +209,21 @@ func generateFileName(line string) string { } ret := strings.Join(chunks, "-") - return ret[:int(math.Min(float64(len(ret)), float64(maxEntryFilenameLength)))] + + if len(ret) > maxEntryFilenameLength { + return ret[:maxEntryFilenameLength] + } + return ret } -func directoryContents(dirPath string) []os.FileInfo { +func directoryContents(dirPath string) ([]os.FileInfo, error) { contents, err := ioutil.ReadDir(dirPath) if err != nil && !os.IsNotExist(err) { - log.Fatalf("couldn't read directory %s: %v", dirPath, err) + return nil, fmt.Errorf("couldn't read directory %s: %v", dirPath, err) } if len(contents) == 0 { - return nil + return nil, nil } // Filter out hidden files @@ -164,16 +237,19 @@ func directoryContents(dirPath string) []os.FileInfo { contents[i] = nil } - return newContents + return newContents, nil } -func generateChangelog(version string) { +func generateChangelog(version string) error { 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) + files, err := directoryContents(path) + if err != nil { + return err + } if len(files) == 0 { continue } @@ -188,22 +264,27 @@ func generateChangelog(version string) { verboseLog.Println("processing", f.Name()) filename := filepath.Join(path, f.Name()) if err := indentAndPrintFile(filename); err != nil { - log.Fatal(err) + return err } } fmt.Println() } } + return nil } -func pruneEmptyDirectories() { - for sectionDir, _ := range sections { - for stanzaDir, _ := range stanzas { - mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir, stanzaDir)) +func pruneEmptyDirectories() error { + for sectionDir := range sections { + for stanzaDir := range stanzas { + err := mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir, stanzaDir)) + if err != nil { + return err + } } - mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir)) + return mustPruneDirIfEmpty(filepath.Join(entriesDir, sectionDir)) } + return nil } // nolint: errcheck @@ -238,45 +319,47 @@ func indentAndPrintFile(filename string) error { } // nolint: errcheck -func writeEntryFile(filename string, bs []byte) { +func writeEntryFile(filename string, bs []byte) error { if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { - log.Fatal(err) + return err } outFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) if err != nil { - log.Fatal(err) + return err } defer outFile.Close() if _, err := outFile.Write(bs); err != nil { - log.Fatal(err) + return 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.") + return nil } -func validateSectionStanzaDirs(sectionDir, stanzaDir string) { +func validateSectionStanzaDirs(sectionDir, stanzaDir string) error { if _, ok := sections[sectionDir]; !ok { - log.Fatalf("invalid section -- %s", sectionDir) + return fmt.Errorf("invalid section -- %s", sectionDir) } if _, ok := stanzas[stanzaDir]; !ok { - log.Fatalf("invalid stanza -- %s", stanzaDir) + return fmt.Errorf("invalid stanza -- %s", stanzaDir) } + return nil } // nolint: errcheck -func readUserInput() []byte { +func readUserInputFromEditor() ([]byte, error) { tempfilename, err := launchUserEditor() if err != nil { - log.Fatalf("couldn't open an editor: %v", err) + return []byte{}, fmt.Errorf("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 []byte{}, fmt.Errorf("error: %v", err) } - return bs + return bs, nil } // nolint: errcheck @@ -296,7 +379,7 @@ func launchUserEditor() (string, error) { "VISUAL or EDITOR variables is set and pointing to a correct editor") } - tempfile, err := ioutil.TempFile("", "sdkch_*") + tempfile, err := ioutil.TempFile("", "clog_*") tempfilename := tempfile.Name() if err != nil { return "", err @@ -317,62 +400,28 @@ func launchUserEditor() (string, error) { return "", err } if fileInfo.Size() == 0 { - log.Fatal("aborting due to empty message") + return "", errors.New("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 mustPruneDirIfEmpty(path string) error { + contents, err := directoryContents(path) + if err != nil { + return err } -} - -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) + if len(contents) != 0 { + return nil + } + if err := os.Remove(path); err != nil { + if !os.IsNotExist(err) { + return err + } + return nil + } + log.Println(path, "removed") + return nil } // DONTCOVER diff --git a/cmd/gosum/main.go b/contrib/devtools/gosum/main.go similarity index 100% rename from cmd/gosum/main.go rename to contrib/devtools/gosum/main.go diff --git a/contrib/install-golangci-lint.sh b/contrib/devtools/install-golangci-lint.sh similarity index 100% rename from contrib/install-golangci-lint.sh rename to contrib/devtools/install-golangci-lint.sh diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 09829e1dd8..e5613e302e 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -7,11 +7,12 @@ import ( "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/crypto/keys/mintkey" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" ) func init() { diff --git a/crypto/keys/mintkey/mintkey.go b/crypto/keys/mintkey/mintkey.go index 11743013a5..4806bcc4dd 100644 --- a/crypto/keys/mintkey/mintkey.go +++ b/crypto/keys/mintkey/mintkey.go @@ -8,7 +8,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/armor" - "github.com/tendermint/tendermint/crypto/encoding/amino" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" cmn "github.com/tendermint/tendermint/libs/common" diff --git a/crypto/keys/mintkey/mintkey_test.go b/crypto/keys/mintkey/mintkey_test.go index 86df52366c..b4ce4d6a0f 100644 --- a/crypto/keys/mintkey/mintkey_test.go +++ b/crypto/keys/mintkey/mintkey_test.go @@ -3,11 +3,12 @@ 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" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" ) func TestArmorUnarmorPrivKey(t *testing.T) { diff --git a/crypto/ledger_mock.go b/crypto/ledger_mock.go index 75e6941da1..b9f1048cf5 100644 --- a/crypto/ledger_mock.go +++ b/crypto/ledger_mock.go @@ -6,12 +6,13 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/tests" bip39 "github.com/cosmos/go-bip39" "github.com/pkg/errors" secp256k1 "github.com/tendermint/btcd/btcec" "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/tests" ) // If ledger support (build tag) has been enabled, which implies a CGO dependency, diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger_secp256k1.go index fae0c1569b..84ddf46578 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger_secp256k1.go @@ -5,6 +5,7 @@ import ( "os" "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" "github.com/cosmos/cosmos-sdk/types" diff --git a/crypto/ledger_test.go b/crypto/ledger_test.go index ea2bc9681c..faa3fd6797 100644 --- a/crypto/ledger_test.go +++ b/crypto/ledger_test.go @@ -7,11 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/tests" tmcrypto "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/encoding/amino" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" + + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" ) func TestLedgerErrorHandling(t *testing.T) { diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index 3999025c21..bce2af93e4 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -1,17 +1,13 @@ ### `cosmos/cosmos-sdk` Release Process -- [ ] 1. Decide on release designation (are we doing a patch, or minor version bump) and start a branch for the release -- [ ] 2. Add commits/PRs that are desired for this release **that haven’t already been added to develop** -- [ ] 3. Merge items in `PENDING.md` into the `CHANGELOG.md`. While doing this make sure that each entry contains links to issues/PRs for each item -- [ ] 4. Summarize breaking API changes section under “Breaking Changes” section to the `CHANGELOG.md` to bring attention to any breaking API changes that affect RPC consumers. -- [ ] 5. Tag the commit `git tag -a { .Release.Name }-rcN -m 'Release { .Release.Name }'` -- [ ] 6. Open PRs for both `master` and `develop`. From now onwards ***no additional PRs targeting develop should be merged**. Additional commits can be added on top of the release branch though. -- [ ] 7. Ensure both `master` and `develop` PRs ***pass tests***. -- [ ] 8. Kick off 1 day of automated fuzz testing -- [ ] 9. Release Lead assigns 2 people to perform [buddy testing script](/docs/RELEASE_TEST_SCRIPT.md) and update the relevant documentation -- [ ] 10. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`) -- [ ] 11. After #6 and #7 have successfully completed then merge the release PR create the final release annotated tag: - - `git tag -a -m { .Release.Name } 'Release { .Release.Name }' - - Merge **the release tag** to both `master` and `develop` so that both branches sit on top of the same commit: `branches='master develop' ; for b in $branches ; do git checkout $b ; git merge { .Release.Name } ; git push $b`. - Alternatively merge both the aforementioned release PRs. - - Push the final annotated release tag: `git push --tags` +Unless otherwise stated, all of the following tasks will be undertaken by the release manager/coordinator: + +- [ ] 1. Decide on release designation (are we doing a patch, or minor version bump) +- [ ] 2. Ensure that all commits/PRs which are destined for this release are merged to the `master` branch +- [ ] 3. Create the release candidate branch `rcN` (please start with `N=1`) (going forward known as **RC**) and ensure it's protected against pushing from anyone except the release manager/coordinator. **no PRs targeting this branch should be merged unless exceptional circumstances arise** +- [ ] 4. On the `RC` branch, use `clog` to prepare the `CHANGELOG.md`. +- [ ] 5. Summarize breaking API changes section under “Breaking Changes” section to the `CHANGELOG.md` to bring attention to any breaking API changes that affect RPC consumers. +- [ ] 6. Kick off a large round of simulation testing (e.g. 400 seeds for 2k blocks) +- [ ] 7. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`) +- [ ] 8. After #6 and #7 have successfully completed create the release branch from the `RC` branch (this will trigger the automated relase process which will build binaries, tag the release and push to github) +- [ ] 9. Merge the release branch to `master` and delete the `RC` branch diff --git a/docs/_attic/sdk/sdk-by-examples/simple-governance/running-the-application.md b/docs/_attic/sdk/sdk-by-examples/simple-governance/running-the-application.md index 3a057054d4..e4acea3411 100644 --- a/docs/_attic/sdk/sdk-by-examples/simple-governance/running-the-application.md +++ b/docs/_attic/sdk/sdk-by-examples/simple-governance/running-the-application.md @@ -7,7 +7,6 @@ Once you have finallized your application, install it using `go get`. The follow ```bash go get github.com//cosmos-sdk cd $GOPATH/src/github.com//cosmos-sdk -make get_vendor_deps make install make install_examples ``` diff --git a/docs/clients/lite/getting_started.md b/docs/clients/lite/getting_started.md index 7f5099402f..f20441c6c4 100644 --- a/docs/clients/lite/getting_started.md +++ b/docs/clients/lite/getting_started.md @@ -10,7 +10,7 @@ To start a REST server, we need to specify the following parameters: | trust-node | bool | "false" | true | Whether this LCD is connected to a trusted full node | | trust-store | DIRECTORY | "$HOME/.lcd" | false | directory for save checkpoints and validator sets | -For example:: +For example: ```bash gaiacli rest-server --chain-id=test \ diff --git a/docs/clients/lite/specification.md b/docs/clients/lite/specification.md index 032f840a59..1b6d73f6c3 100644 --- a/docs/clients/lite/specification.md +++ b/docs/clients/lite/specification.md @@ -62,7 +62,7 @@ type KeyExistsProof struct { } ``` -The data structure of exist proof is shown as above. The process to build and verify existance proof +The data structure of exist proof is shown as above. The process to build and verify existence proof is shown as follows: ![Exist Proof](./pics/existProof.png) diff --git a/docs/clients/service-providers.md b/docs/clients/service-providers.md index b563153f15..b31c2d9be6 100644 --- a/docs/clients/service-providers.md +++ b/docs/clients/service-providers.md @@ -18,9 +18,9 @@ There are three main pieces to consider: We will describe the steps to run and interract with a full-node for the Cosmos Hub. For other SDK-based blockchain, the process should be similar. -First, you need to [install the software](../getting-started/installation.md). +First, you need to [install the software](../cosmos-hub/installation.md). -Then, you can start [running a full-node](../getting-started/join-testnet.md). +Then, you can start [running a full-node](../cosmos-hub/join-testnet.md). ### Command-Line interface @@ -34,12 +34,13 @@ To generate a new key (default secp256k1 elliptic curve): gaiacli keys add ``` -You will be asked to create a passwords (at least 8 characters) for this key-pair. The command returns 4 informations: +You will be asked to create a passwords (at least 8 characters) for this key-pair. The command returns 5 informations: - `NAME`: Name of your key +- `TYPE`: Type of your key, always `local`. - `ADDRESS`: Your address. Used to receive funds. - `PUBKEY`: Your public key. Useful for validators. -- `Seed phrase`: 12-words phrase. **Save this seed phrase somewhere safe**. It is used to recover your private key in case you forget the password. +- `MNEMONIC`: 24-words phrase. **Save this mnemonic somewhere safe**. It is used to recover your private key in case you forget the password. You can see all your available keys by typing: @@ -62,14 +63,20 @@ gaiacli account Here is the command to send coins via the CLI: ```bash -gaiacli send --amount=10faucetToken --chain-id= --from= --to= +gaiacli tx send \ + --chain-id= \ + --from= ``` +Parameters: + +- ``: Address of the recipient. +- ``: This parameter accepts the format ``, such as `10faucetToken`. + Flags: -- `--amount`: This flag accepts the format ``. + - `--chain-id`: This flag allows you to specify the id of the chain. There will be different ids for different testnet chains and main chain. - `--from`: Name of the key of the sending account. -- `--to`: Address of the recipient. #### Help @@ -88,12 +95,12 @@ The Rest Server acts as an intermediary between the front-end and the full-node. To start the Rest server: ```bash -gaiacli advanced rest-server --node= +gaiacli rest-server --node= ``` Flags: - `--trust-node`: A boolean. If `true`, light-client verification is disabled. If `false`, it is disabled. For service providers, this should be set to `true`. By default, it set to `true`. -- `--node`: This is where you indicate the address and the port of your full-node. The format is . If the full-node is on the same machine, the address should be `tcp://localhost:26657`. +- `--node`: This is where you indicate the address and the port of your full-node. The format is ``. If the full-node is on the same machine, the address should be `tcp://localhost:26657`. - `--laddr`: This flag allows you to specify the address and port for the Rest Server (default `1317`). You will mostly use this flag only to specify the port, in which case just input "localhost" for the address. The format is . @@ -101,7 +108,7 @@ Flags: The recommended way to listen for incoming transaction is to periodically query the blockchain through the following endpoint of the LCD: -[`/bank/balance/{account}`](https://cosmos.network/rpc/#/ICS20/get_bank_balances__address_) +[`/bank/balance/{address}`](https://cosmos.network/rpc/#/ICS20/get_bank_balances__address_) ## Rest API diff --git a/docs/cosmos-hub/gaiacli.md b/docs/cosmos-hub/gaiacli.md index f502554d77..99e79b32aa 100644 --- a/docs/cosmos-hub/gaiacli.md +++ b/docs/cosmos-hub/gaiacli.md @@ -201,13 +201,12 @@ When you query an account balance with zero tokens, you will get this error: `No The following command could be used to send coins from one account to another: ```bash -gaiacli tx send 10faucetToken \ - --chain-id= \ - --from= \ +gaiacli tx send 10faucetToken \ + --chain-id= ``` ::: warning Note -The `--amount` flag accepts the format `--amount=`. +The `amount` argument accepts the format ``. ::: ::: tip Note @@ -233,9 +232,8 @@ You can simulate a transaction without actually broadcasting it by appending the `--dry-run` flag to the command line: ```bash -gaiacli tx send 10faucetToken \ +gaiacli tx send 10faucetToken \ --chain-id= \ - --from= \ --dry-run ``` @@ -243,9 +241,8 @@ Furthermore, you can build a transaction and print its JSON format to STDOUT by appending `--generate-only` to the list of the command line arguments: ```bash -gaiacli tx send 10faucetToken \ +gaiacli tx send 10faucetToken \ --chain-id= \ - --from= \ --generate-only > unsignedSendTx.json ``` @@ -258,6 +255,7 @@ gaiacli tx sign \ ::: tip Note The `--generate-only` flag prevents `gaiacli` from accessing the local keybase. +Thus when such flag is supplied `` must be an address. ::: You can validate the transaction's signatures by typing the following: diff --git a/docs/cosmos-hub/installation.md b/docs/cosmos-hub/installation.md index 48f0f71a74..20e5ffa44a 100644 --- a/docs/cosmos-hub/installation.md +++ b/docs/cosmos-hub/installation.md @@ -9,8 +9,8 @@ Install `go` by following the [official docs](https://golang.org/doc/install). R ```bash mkdir -p $HOME/go/bin echo "export GOPATH=$HOME/go" >> ~/.bash_profile -echo "export GOBIN=$GOPATH/bin" >> ~/.bash_profile -echo "export PATH=$PATH:$GOBIN" >> ~/.bash_profile +echo "export GOBIN=\$GOPATH/bin" >> ~/.bash_profile +echo "export PATH=\$PATH:\$GOBIN" >> ~/.bash_profile source ~/.bash_profile ``` diff --git a/docs/cosmos-hub/validators/kms/kms.md b/docs/cosmos-hub/validators/kms/kms.md new file mode 100644 index 0000000000..9abeaa7e01 --- /dev/null +++ b/docs/cosmos-hub/validators/kms/kms.md @@ -0,0 +1,34 @@ +# KMS - Key Management System + +[Tendermint KMS](https://github.com/tendermint/kms) is a key management service that allows separating key management from Tendermint nodes. In addition it provides other advantages such as: + +- Improved security and risk management policies +- Unified API and support for various HSM (hardware security modules) +- Double signing protection (software or hardware based) + +It is recommended that the KMS service runs in a separate physical hosts. + +## Building + +Detailed build instructions can be found [here](https://github.com/tendermint/kms#installation). + +::: tip +When compiling the KMS, ensure you have enabled the applicable features: +::: + +| Backend | Recommended Command line | +|-----------------------|---------------------------------------| +| YubiHSM | ```cargo build --features yubihsm``` | +| Ledger+Tendermint App | ```cargo build --features ledgertm``` | + +## Configuration + +A KMS can be configured in various ways: + +### Using a YubiHSM + + Detailed information on how to setup a KMS with YubiHSM2 can be found [here](https://github.com/tendermint/kms/blob/master/README.yubihsm.md) + +### Using a Ledger device running the Tendermint app + + Detailed information on how to setup a KMS with Ledger Tendermint App can be found [here](kms_ledger.md) diff --git a/docs/cosmos-hub/validators/kms/kms_ledger.md b/docs/cosmos-hub/validators/kms/kms_ledger.md new file mode 100644 index 0000000000..8241d2d1e9 --- /dev/null +++ b/docs/cosmos-hub/validators/kms/kms_ledger.md @@ -0,0 +1,113 @@ +# Setting up Tendermint KMS + Ledger + +::: danger Warning +The following instructions are a brief walkthrough and not a comprehensive guideline. You should consider and [research more about the security implications](./security.md) of activating an external KMS. +::: + +::: danger Warning +KMS and Ledger Tendermint app are currently work in progress. Details may vary. Use with care under your own risk. +::: + +## Tendermint Validator app (for Ledger devices) + +You should be able to find the Tendermint app in Ledger Live. + +*Note: at the moment, you might need to enable `developer mode` in Ledger Live settings* + +## KMS configuration + +In this section, we will configure a KMS to use a Ledger device running the Tendermint Validator App. + +### Config file + +You can find other configuration examples [here](https://github.com/tendermint/kms/blob/master/tmkms.toml.example) + +- Create a `~/.tmkms/tmkms.toml` file with the following content (use an adequate `chain_id`) + +```toml +# Example KMS configuration file +[[validator]] +addr = "tcp://localhost:26658" # or "unix:///path/to/socket" +chain_id = "gaia-11001" +reconnect = true # true is the default +secret_key = "~/.tmkms/secret_connection.key" + +[[providers.ledgertm]] +chain_ids = ["gaia-11001"] +``` + +- Edit `addr` to point to your `gaiad` instance. +- Adjust `chain-id` to match your `.gaiad/config/config.toml` settings. +- `provider.ledgertm` has not additional parameters at the moment, however, it is important that you keep that header to enable the feature. + +*Plug your Ledger device and open the Tendermint validator app.* + +### Generate secret key + +Now you need to generate secret_key: + +```bash +tmkms keygen ~/.tmkms/secret_connection.key +``` + +### Retrieve validator key + +The last step is to retrieve the validator key that you will use in `gaiad`. + +Start the KMS: + +```bash +tmkms start -c ~/.tmkms/tmkms.toml +``` + +The output should look similar to: + +```text +07:28:24 [INFO] tmkms 0.3.0 starting up... +07:28:24 [INFO] [keyring:ledgertm:ledgertm] added validator key cosmosvalconspub1zcjduepqy53m39prgp9dz3nz96kaav3el5e0th8ltwcf8cpavqdvpxgr5slsd6wz6f +07:28:24 [INFO] KMS node ID: 1BC12314E2E1C29015B66017A397F170C6ECDE4A +``` + +The KMS may complain that it cannot connect to gaiad. That is fine, we will fix it in the next section. + +This output indicates the validator key linked to this particular device is: `cosmosvalconspub1zcjduepqy53m39prgp9dz3nz96kaav3el5e0th8ltwcf8cpavqdvpxgr5slsd6wz6f` + +Take note of the validator pubkey that appears in your screen. *We will use it in the next section.* + +## Gaia configuration + +You need to enable KMS access by editing `.gaiad/config/config.toml`. In this file, modify `priv_validator_laddr` to create a listening address/port or a unix socket in `gaiad`. + +For example: + +```toml +... +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "tcp://127.0.0.1:26658" +... +``` + +Let's assume that you have set up your validator account and called it `kmsval`. You can tell gaiad the key that we've got in the previous section. + +```bash +gaiad gentx --name kmsval --pubkey {.ValidatorKey} +``` + +Now start `gaiad`. You should see that the KMS connects and receives a signature request. + +Once the ledger receives the first message, it will ask for confirmation that the values are adequate. + +![](ledger_1.jpg) + +Click the right button, if the height and round are correct. + +After that, you will see that the KMS will start forwarding all signature requests to the ledger: + +![](ledger_2.jpg) + +::: danger Warning +The word TEST in the second picture, second line appears because they were taken on a pre-release version. + +Once the app as been released in Ledger's app store, this word should NOT appear. +::: diff --git a/docs/cosmos-hub/validators/kms/ledger_1.jpg b/docs/cosmos-hub/validators/kms/ledger_1.jpg new file mode 100644 index 0000000000..100175e1a7 Binary files /dev/null and b/docs/cosmos-hub/validators/kms/ledger_1.jpg differ diff --git a/docs/cosmos-hub/validators/kms/ledger_2.jpg b/docs/cosmos-hub/validators/kms/ledger_2.jpg new file mode 100644 index 0000000000..9c6fa6b1a6 Binary files /dev/null and b/docs/cosmos-hub/validators/kms/ledger_2.jpg differ diff --git a/docs/cosmos-hub/validators/security.md b/docs/cosmos-hub/validators/security.md index cb822d45f4..4127b22377 100644 --- a/docs/cosmos-hub/validators/security.md +++ b/docs/cosmos-hub/validators/security.md @@ -25,6 +25,7 @@ Sentry nodes can be quickly spun up or change their IP addresses. Because the li To setup your sentry node architecture you can follow the instructions below: Validators nodes should edit their config.toml: + ```bash # Comma separated list of nodes to keep persistent connections to # Do not add private peers to this list if you don't want them advertised @@ -35,6 +36,7 @@ pex = false ``` Sentry Nodes should edit their config.toml: + ```bash # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) # Example ID: 3e16af0cead27979e1fc3dac57d03df3c7a77acc@3.87.179.235:26656 @@ -50,5 +52,5 @@ By default, uppercase environment variables with the following prefixes will rep - `TM` (for Tendermint flags) - `BC` (for democli or basecli flags) -For example, the environment variable `GA_CHAIN_ID` will map to the command line flag `--chain-id`. Note that while explicit command-line flags will take precedence over environment variables, environment variables will take precedence over any of your configuration files. For this reason, it's imperative that you lock down your environment such that any critical parameters are defined as flags on the CLI or prevent modification of any environment variables. - +For example, the environment variable `GA_CHAIN_ID` will map to the command line flag `--chain-id`. Note that while explicit command-line flags will take precedence over environment variables, environment variables will take precedence over any of your configuration files. For this reason, it's imperative that you lock down your environment such that any critical parameters are defined as flags on the CLI or prevent modification of any environment variables. + \ No newline at end of file diff --git a/docs/cosmos-hub/validators/validator-setup.md b/docs/cosmos-hub/validators/validator-setup.md index 0087e8f8e8..c0dfd28821 100644 --- a/docs/cosmos-hub/validators/validator-setup.md +++ b/docs/cosmos-hub/validators/validator-setup.md @@ -6,6 +6,8 @@ Information on how to join the mainnet (`genesis.json` file and seeds) is held [ Before setting up your validator node, make sure you've already gone through the [Full Node Setup](../join-mainnet.md) guide. +If you plan to use a KMS (key management system), you should go through these steps first: [Using a KMS](kms/kms.md). + ## What is a Validator? [Validators](./overview.md) are responsible for committing new blocks to the blockchain through voting. A validator's stake is slashed if they become unavailable or sign blocks at the same height. Please read about [Sentry Node Architecture](./validator-faq.md#how-can-validators-protect-themselves-from-denial-of-service-attacks) to protect your node from DDOS attacks and to ensure high-availability. @@ -165,6 +167,15 @@ You should now see your validator in one of the Cosmos Hub explorers. You are lo To be in the validator set, you need to have more total voting power than the 100th validator. ::: +## Halting Your Validator + +When attempting to perform routine maintenance or planning for an upcoming coordinated +upgrade, it can be useful to have your validator systematically and gracefully halt. +You can achieve this by either setting the `halt-height` to the height at which +you want your node to shutdown or by passing the `--halt-height` flag to `gaiad`. +The node will shutdown with a zero exit code at that given height after committing +the block. + ## Common Problems ### Problem #1: My validator has `voting_power: 0` diff --git a/docs/spec/bank/04_tags.md b/docs/spec/bank/04_tags.md index 103c598390..0a7f382c2a 100644 --- a/docs/spec/bank/04_tags.md +++ b/docs/spec/bank/04_tags.md @@ -6,7 +6,18 @@ The bank module emits the following events/tags: ### MsgSend -| Key | Value | -|-----------|---------------------------| -| sender | {senderAccountAddress} | -| recipient | {recipientAccountAddress} | +| Key | Value | +|-------------|---------------------------| +| `action` | `send` | +| `category` | `bank` | +| `sender` | {senderAccountAddress} | +| `recipient` | {recipientAccountAddress} | + +### MsgMultiSend + +| Key | Value | +|-------------|---------------------------| +| `action` | `multisend` | +| `category` | `bank` | +| `sender` | {senderAccountAddress} | +| `recipient` | {recipientAccountAddress} | diff --git a/docs/spec/crisis/03_tags.md b/docs/spec/crisis/03_tags.md index f3509bbdac..a6a4a738ca 100644 --- a/docs/spec/crisis/03_tags.md +++ b/docs/spec/crisis/03_tags.md @@ -6,8 +6,9 @@ The crisis module emits the following events/tags: ### MsgVerifyInvariance -| Key | Value | -|-----------|---------------------| -| action | verify_invariant | -| sender | {message-sender} | -| invariant | {invariant-route} | +| Key | Value | +|-------------|--------------------| +| `action` | `verify_invariant` | +| `category` | `crisis` | +| `sender` | {message-sender} | +| `invariant` | {invariant-route} | diff --git a/docs/spec/distribution/06_tags.md b/docs/spec/distribution/06_tags.md index c526ae0c27..5e0592f008 100644 --- a/docs/spec/distribution/06_tags.md +++ b/docs/spec/distribution/06_tags.md @@ -6,25 +6,25 @@ The distribution module emits the following events/tags: ### MsgSetWithdrawAddress -| Key | Value | -|-----------|---------------------------| -| delegator | {delegatorAccountAddress} | - -### MsgWithdrawDelegatorRewardsAll - -| Key | Value | -|-----------|---------------------------| -| delegator | {delegatorAccountAddress} | +| Key | Value | +|------------|---------------------------| +| `action` | `set_withdraw_address` | +| `category` | `distribution` | +| `sender` | {delegatorAccountAddress} | ### MsgWithdrawDelegatorReward -| Key | Value | -|------------------|---------------------------| -| delegator | {delegatorAccountAddress} | -| source-validator | {srcOperatorAddress} | +| Key | Value | +|--------------------|-----------------------------| +| `action` | `withdraw_delegator_reward` | +| `category` | `distribution` | +| `sender` | {delegatorAccountAddress} | +| `source-validator` | {srcOperatorAddress} | -### MsgWithdrawValidatorRewardsAll +### MsgWithdrawValidatorCommission -| Key | Value | -|------------------|----------------------| -| source-validator | {srcOperatorAddress} | +| Key | Value | +|------------|---------------------------------| +| `action` | `withdraw_validator_commission` | +| `category` | `distribution` | +| `sender` | {srcOperatorAddress} | diff --git a/docs/spec/governance/04_tags.md b/docs/spec/governance/04_tags.md index 6c97a2b49d..fb547e0ee8 100644 --- a/docs/spec/governance/04_tags.md +++ b/docs/spec/governance/04_tags.md @@ -4,35 +4,38 @@ The governance module emits the following events/tags: ## EndBlocker -| Key | Value | -|-----------------|------------------------------------------------------| -| proposal-result | proposal-passed\|proposal-rejected\|proposal-dropped | +| Key | Value | +|-------------------|------------------------------------------------------------| +| `proposal-result` | `proposal-passed`\|`proposal-rejected`\|`proposal-dropped` | ## Handlers ### MsgSubmitProposal -| Key | Value | -|-------------------------|--------------------------| -| action | proposal-vote | -| proposer | {proposerAccountAddress} | -| proposal-id | {proposalID} | -| voting-period-start [0] | {proposalID} | +| Key | Value | +|---------------------------|--------------------------| +| `action` | `submit_proposal` | +| `category` | `governance` | +| `sender` | {proposerAccountAddress} | +| `proposal-id` | {proposalID} | +| `voting-period-start` [0] | {proposalID} | * [0] Tag only emitted if the voting period starts during the submission. ### MsgVote -| Key | Value | -|-------------|-----------------------| -| action | proposal-vote | -| voter | {voterAccountAddress} | -| proposal-id | {proposalID} | +| Key | Value | +|---------------|-----------------------| +| `action` | `vote` | +| `category` | `governance` | +| `sender` | {voterAccountAddress} | +| `proposal-id` | {proposalID} | ### MsgDeposit -| Key | Value | -|-------------|---------------------------| -| action | proposal-vote | -| depositor | {depositorAccountAddress} | -| proposal-id | {proposalID} | +| Key | Value | +|---------------|---------------------------| +| `action` | `deposit` | +| `category` | `governance` | +| `sender` | {depositorAccountAddress} | +| `proposal-id` | {proposalID} | diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index 344f69c7d7..faff100c47 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -8,7 +8,7 @@ The IBC protocol creates a mechanism by which two replicated fault-tolerant stat The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an ordered message queue primitive, allowing the creation of complex inter-chain processes without trusted third parties. -The message packets are not signed by one psuedonymous account, or even multiple, as in multi-signature sidechain implementations. Rather, IBC assigns authorization of the packets to the source blockchain's consensus algorithm, performing light-client style verification on the destination chain. The Byzantine-fault-tolerant properties of the underlying blockchains are preserved: a user transferring assets between two chains using IBC must trust only the consensus algorithms of both chains. +The message packets are not signed by one pseudonymous account, or even multiple, as in multi-signature sidechain implementations. Rather, IBC assigns authorization of the packets to the source blockchain's consensus algorithm, performing light-client style verification on the destination chain. The Byzantine-fault-tolerant properties of the underlying blockchains are preserved: a user transferring assets between two chains using IBC must trust only the consensus algorithms of both chains. In this paper, we define a process of posting block headers and Merkle tree proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee ordered delivery. We then explain how to handle packet receipts (response/error) on the source chain, which enables the creation of asynchronous RPC-like protocols on top of IBC. Finally, we detail some optimizations and how to handle Byzantine blockchains. diff --git a/docs/spec/slashing/06_tags.md b/docs/spec/slashing/06_tags.md index a9f2e59d3a..8ba7e1db98 100644 --- a/docs/spec/slashing/06_tags.md +++ b/docs/spec/slashing/06_tags.md @@ -6,7 +6,8 @@ The slashing module emits the following events/tags: ### MsgUnjail -| Key | Value | -|-----------|----------------------------| -| action | validator-unjailed | -| validator | {validatorOperatorAddress} | +| Key | Value | +|------------|----------------------------| +| `action` | `unjail` | +| `category` | `slashing` | +| `sender` | {validatorOperatorAddress} | diff --git a/docs/spec/staking/06_tags.md b/docs/spec/staking/06_tags.md index c2efd3b377..bf64b9d560 100644 --- a/docs/spec/staking/06_tags.md +++ b/docs/spec/staking/06_tags.md @@ -4,55 +4,62 @@ The staking module emits the following events/tags: ## EndBlocker -| Key | Value | -|-----------------------|-------------------------------------------| -| action | complete-unbonding\|complete-redelegation | -| delegator | {delegatorAccountAddress} | -| source-validator | {srcOperatorAddress} | -| destination-validator | {dstOperatorAddress} | +| Key | Value | +|-------------------------|-----------------------------------------------| +| `action` | `complete-unbonding`\|`complete-redelegation` | +| `category` | `staking` | +| `delegator` | {delegatorAccountAddress} | +| `source-validator` | {srcOperatorAddress} | +| `destination-validator` | {dstOperatorAddress} | ## Handlers ### MsgCreateValidator -| Key | Value | -|-----------------------|----------------------| -| destination-validator | {dstOperatorAddress} | -| moniker | {validatorMoniker} | -| identity | {validatorIdentity} | +| Key | Value | +|------------|----------------------| +| `action` | `create_validator` | +| `category` | `staking` | +| `sender` | {dstOperatorAddress} | ### MsgEditValidator -| Key | Value | -|-----------------------|----------------------| -| destination-validator | {dstOperatorAddress} | -| moniker | {validatorMoniker} | -| identity | {validatorIdentity} | +| Key | Value | +|------------|----------------------| +| `action` | `edit_validator` | +| `category` | `staking` | +| `sender` | {dstOperatorAddress} | ### MsgDelegate -| Key | Value | -|-----------------------|-------------------------------------------| -| delegator | {delegatorAccountAddress} | -| destination-validator | {dstOperatorAddress} | +| Key | Value | +|-------------------------|---------------------------| +| `action` | `delegate` | +| `category` | `staking` | +| `sender` | {delegatorAccountAddress} | +| `destination-validator` | {dstOperatorAddress} | ### MsgBeginRedelegate -| Key | Value | -|-----------------------|-------------------------------------------| -| delegator | {delegatorAccountAddress} | -| source-validator | {srcOperatorAddress} | -| destination-validator | {dstOperatorAddress} | -| end-time [0] | {delegationFinishTime} | +| Key | Value | +|-------------------------|---------------------------| +| `action` | `begin_redelegate` | +| `category` | `staking` | +| `sender` | {delegatorAccountAddress} | +| `source-validator` | {srcOperatorAddress} | +| `destination-validator` | {dstOperatorAddress} | +| `end-time` [0] | {delegationFinishTime} | * [0] Time is formatted in the RFC3339 standard ### MsgUndelegate -| Key | Value | -|------------------|---------------------------| -| delegator | {delegatorAccountAddress} | -| source-validator | {srcOperatorAddress} | -| end-time [0] | {delegationFinishTime} | +| Key | Value | +|--------------------|---------------------------| +| `action` | `begin_unbonding` | +| `category` | `staking` | +| `sender` | {delegatorAccountAddress} | +| `source-validator` | {srcOperatorAddress} | +| `end-time` [0] | {delegationFinishTime} | * [0] Time is formatted in the RFC3339 standard diff --git a/docs/translations/cn/clients/README.md b/docs/translations/cn/clients/README.md new file mode 100644 index 0000000000..866a2d1bc3 --- /dev/null +++ b/docs/translations/cn/clients/README.md @@ -0,0 +1,19 @@ +# 客户端 + +本节说明包含区块链 SDK 的客户端的信息。 + +> *注*:此部分仍在开发中。 + +##轻客户端 + +轻客户端使用户能够与您的应用程序进行交互,且无需下载整个状态历史记录,但具有良好的安全性。 + + - [轻客户端概述](./lite/README.md) + - [启动轻客户端服务器](./lite/getting_started.md) + - [轻客户端规范](./lite/specification.md) + +##其他客户端 + + - [区块链 SDK 的 CLI](./cli.md) + - [服务提供商文档](./service-providers.md) + diff --git a/docs/translations/cn/clients/cli.md b/docs/translations/cn/clients/cli.md new file mode 100644 index 0000000000..944e14746e --- /dev/null +++ b/docs/translations/cn/clients/cli.md @@ -0,0 +1,4 @@ +# CLI + +> TODO: Rewrite this section to explain how CLI works for a generic SDK app. + diff --git a/docs/translations/cn/clients/lite/README.md b/docs/translations/cn/clients/lite/README.md new file mode 100644 index 0000000000..0a286a6022 --- /dev/null +++ b/docs/translations/cn/clients/lite/README.md @@ -0,0 +1,60 @@ +# 轻客户端概览 + +**点击[这里](https://cosmos.network/rpc/)查看Cosmos SDK 轻客户端 RPC 文档** + +## 简介 + +轻客户端允许客户端(例如移动电话)从任何全节点接收区块链状态的证明。 轻客户端不必信任任何全节点,因为他们能够验证他们收到的任何证明,因此全节点不能对网络状态撒谎。 + +轻客户端可以用最低的带宽、计算和存储资源提供与全节点相同的安全性。 同时,它还可以根据用户的配置提供模块化功能。 这些出色的功能允许开发人员构建完全安全、高效且可用的移动应用、网站或任何其他应用程序,而无需部署或维护任何完整的区块链节点。 + +### 什么是轻节点 + +Cosmos SDK 轻节点(Gaia-lite)分为两个独立的组件。 第一个组件对于任何基于 Tendermint 的应用程序都是通用的,它处理区块头相关的安全性和连接性,并验证来自全节点的证明与本地可信验证人集合的对比。 此外,它暴露与任何Tendermint 核心节点完全相同的API。 第二个组件专用于 Cosmos Hub(`gaiad`),它作为查询端点工作,并公开特定于应用程序的功能,这些功能可以是任意的。 针对应用程序状态的所有查询都必须通过查询端点。 查询端点的优点是它可以验证应用程序返回的证据。 + +### 高层体系结构 + +想要为 Cosmos Hub(或任何其他 zone)构建第三方客户端应用程序的应用程序开发人员,应根据其规范 API 构建。 该API 是多个部分的组合。 所有 zone 都必须暴露ICS0(TendermintAPI)。 除此之外,任何 zone 都可以自由选择模块 API的任意组合,具体取决于状态机使用的模块。 Cosmos Hub最初将支持[ICS0](https://cosmos.network/rpc/#/ICS0) (TendermintAPI)、 [ICS1](https://cosmos.network/rpc/#/ICS1) (KeyAPI)、 [ICS20](https://cosmos.network/rpc/#/ICS20) (TokenAPI)、 [ICS21](https://cosmos.network/rpc/#/ICS21) (StakingAPI)、 [ICS22](https://cosmos.network/rpc/#/ICS22) (GovernanceAPI) 和 [ICS23](https://cosmos.network/rpc/#/ICS23) (SlashingAPI)。 + +![high-level](../../../../clients/lite/pics/high-level.png) + +预计所有应用程序仅依赖于 Gaia-lite 运行。 Gaia-lite 是唯一一款围绕 zone API 提供稳定性保证的软件。 + +### 对比 + +ABCI 的全节点与其轻客户端的区别在于以下方面: + +| | Full Node | 轻客户端 | Description | +| ------------------------------- | --------------- | ------------- | ------------------------------------------------------------ | +| 执行并验证交易 | Yes | No | 全节点将执行并验证所有交易,而Gaia-lite则不会 | +| 验证和存储区块 | Yes | No | 全节点将验证并保存所有块,而Gaia-lite则不会 | +| 参与共识 | Yes | No | 只有当全节点是验证人时,它才会参与共识。 Lite节点永远不会参与共识。 | +| 带宽开销 | 巨大 | 很小 | 全节点将接收所有块。 如果带宽有限,它将落后于主网络。 更重要的是,如果碰巧是验证人,它将减缓共识过程。 轻客户端需要很少的带宽, 只有在提供本地请求时,才会占用带宽。 | +| 计算资源 | 巨大 | 很小 | 全节点将执行所有交易并验证所有块,这需要大量的计算资源 | +| 存储资源 | 巨大 | 很小 | 全节点将保存所有块和 ABCI 状态,而Gaia-lite只保存验证人集合和一些检查点。 | +| 电力资源 | 巨大 | 很小 | 全节点必须在具有高性能并能一直运行的机器上部署,因此功耗将是巨大的。 Gaia-lite可以部署在与用户应用程序相同的机器上,也可以部署在独立但性能较差的机器上。 此外,Lite客户端可以在必要时随时关闭。所以Gaia-lite只消耗很少的功率,即使移动设备也能满足功率需求。 | +| 提供的 APIs | 所有cosmos APIs | 部分模块 APIs | 全节点支持所有cosmos API。 Gaia-lite 根据用户的配置提供模块化API。 | +| 安全等级 | 高 | 高 | 全节点将自行验证所有交易和块。 轻型客户端无法执行此操作,但它可以查询来自其他全节点的任何数据并独立验证数据。 因此,全节点和轻型客户端都不需要信任任何第三方节点,它们都可以实现高安全性。 | + +根据上表,Gaia-lite 可以满足所有用户的功能和安全需求,但只需要很少的带宽、计算、存储和电力资源。 + +## 安全实现 + +### 可信验证人集合 + +Gaia-lite的基本设计理念遵循两个规则: + +1. **不信任任何区块链节点,包括验证人节点和其他全节点** +2. **只信任整个验证人集合** + +原始的可信验证人集应该预先放置到其信任库中,通常这个验证人集来自 genesis 文件。 在运行时期间,如果 Gaia-lite 检测到不同的验证人集,它将验证它并将可信的验证人集保存到信任库中。 + +![validator-set-change](../../../../clients/lite/pics/validatorSetChange.png) + +### 信任传播 + +从上面的小节中,我们了解了如何获得可信验证器集以及 LCD 如何跟踪验证人集演化。 验证人集是信任的基础,信任可以传播到其他区块链数据,例如块和交易。 传播架构如下所示: + +![change-process](../../../../clients/lite/pics/trustPropagate.png) + +通常,通过可信验证人集,轻客户端可以验证包含所有预提交数据和块头数据的每个提交块。 此时块哈希、数据哈希和应用哈希是可信的。 基于此和默克尔证明,所有交易数据和 ABCI 状态也可以被验证。 \ No newline at end of file diff --git a/docs/translations/cn/clients/lite/getting_started.md b/docs/translations/cn/clients/lite/getting_started.md new file mode 100644 index 0000000000..a6e75acc0c --- /dev/null +++ b/docs/translations/cn/clients/lite/getting_started.md @@ -0,0 +1,23 @@ +# 入门 + +要启动 REST 服务器,我们需要指定以下参数: + +| 参数 | 类型 | 默认值 | 必填 | 描述 | +| ----------- | --------- | ----------------------- | ----- | ---------------------------- | +| chain-id | string | null | true | 要链接全节点的 chain id | +| node | URL | "tcp://localhost:46657" | true | 要链接全节点的地址和端口号 | +| laddr | URL | "tcp://localhost:1317" | true | 提供 REST 服务的地址和端口号 | +| trust-node | bool | "false" | true | 是否信任 LCD 连接的全节点 | +| trust-store | DIRECTORY | "$HOME/.lcd" | false | 保存检查点和验证人集的目录 | + +示例: + +```bash +gaiacli rest-server --chain-id=test \ + --laddr=tcp://localhost:1317 \ + --node tcp://localhost:26657 \ + --trust-node=false +``` + +有关Gaia-Lite RPC的更多信息,请参阅 [swagger documentation](https://cosmos.network/rpc/) + diff --git a/docs/translations/cn/clients/lite/specification.md b/docs/translations/cn/clients/lite/specification.md new file mode 100644 index 0000000000..993aefab23 --- /dev/null +++ b/docs/translations/cn/clients/lite/specification.md @@ -0,0 +1,183 @@ +# 规范 + +该规范描述了如何实现 LCD。 LCD 支持模块化 API。 目前,仅支持ICS0(TendermintAPI),ICS1(密钥API)和ICS20(Key API)。 如有必要,后续可以包含更多API。 + +## 构建并验证 ABCI 状态的证明 + +众所周知,基于 cosmos-sdk 的应用程序的存储包含多个子库。 每个子目录由 IAVL 存储实现。 这些子组件由简单的 Merkle 树组成。 创建树时,我们需要从这些子库中提取名字、高度和存储根哈希以构建一组简单的 Merkle 叶节点,然后计算从叶节点到根的哈希。 简单 Merkle 树的根哈希是 AppHash,它将包含在块头中。 + +![Simple Merkle Tree](../../../../clients/lite/pics/simpleMerkleTree.png) + +正如我们在[LCD信任传播](https://github.com/irisnet/cosmos-sdk/tree/bianjie/lcd_spec/docs/spec/lcd#trust-propagation)中所讨论的那样,可以通过检查针对可信验证人集的投票权来验证 AppHash。 这里我们只需要建立从 ABCI 状态到 AppHash 的证明。 证据包含两部分: + +* IAVL 证明 +* 子库到 AppHash 的证明 + +### IAVL 证明 + +证明有两种类型:存在证明和缺席证明。 如果查询密钥存在于 IAVL 存储中,则它返回键值及其存在证明。 另一方面,如果密钥不存在,那么它只返回缺席证明,这可以证明密钥肯定不存在。 + +### IAVL 存在证明 + +```go +type CommitID struct { + Version int64 + Hash []byte +} + +type storeCore struct { + CommitID CommitID +} + +type MultiStoreCommitID struct { + Name string + Core storeCore +} + +type proofInnerNode struct { + Height int8 + Size int64 + Version int64 + Left []byte + Right []byte +} + +type KeyExistsProof struct { + MultiStoreCommitInfo []MultiStoreCommitID // 所有子库提交id + StoreName string // 当前子库名字 + Height int64 // 当前子库提交高度 + RootHash cmn.HexBytes // 此 IAVL 树的根哈希 + Version int64 // 此 IAVL 树中 key-value 的版本号 + InnerNodes []proofInnerNode // 从根节点到 key-value 叶子节点的路径 +} +``` + +存在证据的数据结构如上所示。 构建和验证存在证明的过程如下所示: + +![Exist Proof](../../../../clients/lite/pics/existProof.png) + +构建证明的步骤: + +* 从根节点访问IAVL树 +* 记录 InnerNodes 中的访问节点 +* 找到目标叶节点后,将叶节点版本赋值给证明版本 +* 将当前 IAVL 树高赋值给证明高度 +* 将当前 IAVL 树根哈希赋值给证明根哈希 +* 将当前的子目录名称赋值给证明 StoreName +* 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo + +验证证明的步骤: + +* 使用证明版本中的键、值构建叶节点 +* 计算叶节点哈希 +* 将哈希值分配给第一个 innerNode 的 rightHash,然后计算第一个 innerNode 哈希值 +* 传播哈希计算过程。 如果先前的 innerNode 是下一个 innerNode 的左子节点,则将先前的 innerNode 散列分配给下一个 innerNode 的左散列。否则,将先前的 innerNode 散列分配给下一个innerNode的右散列 +* 最后 innerNode 的哈希应该等于此证明的根哈希, 否则证明无效。 + +### IAVL 缺席证明 + +众所周知,所有 IAVL 叶节点都按每个叶节点的密钥排序。 因此,我们可以在 IAVL 树的整个密钥集中计算出目标密钥的位置。 如下图所示,我们可以找到左键和右键。 如果我们可以证明左键和右键肯定存在,并且它们是相邻的节点,那么目标密钥肯定不存在。 + +![Absence Proof1](../../../../clients/lite/pics/absence1.png) + +如果目标密钥大于最右边的叶节点或小于最左边的叶子节点,则目标密钥肯定不存在。 + +![Absence Proof2](../../../../clients/lite/pics/absence2.png)![Absence Proof3](../../../../clients/lite/pics/absence3.png) + +```go +type proofLeafNode struct { + KeyBytes cmn.HexBytes + ValueBytes cmn.HexBytes + Version int64 +} + +type pathWithNode struct { + InnerNodes []proofInnerNode + Node proofLeafNode +} + +type KeyAbsentProof struct { + MultiStoreCommitInfo []MultiStoreCommitID + StoreName string + Height int64 + RootHash cmn.HexBytes + Left *pathWithNode // Proof the left key exist + Right *pathWithNode //Proof the right key exist +} +``` + +以上是缺席证明的数据结构。 构建证据的步骤: + +* 从根节点访问IAVL树 +* 获取整个密钥集中密钥的对应索引(标记为INDEX) +* 如果返回的索引等于0,则右索引应为0,且左节点不存在 +* 如果返回的索引等于整个密钥集的大小,则左节点索引应为INDEX-1,且右节点不存在 +* 否则,右节点索引应为INDEX,左节点索引应为INDEX-1 +* 将当前 IAVL 树高赋值给证明高度 +* 将当前 IAVL 树根哈希赋值给证明根哈希 +* 将当前的子目录名称赋值给证明 StoreName +* 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo + +验证证明的步骤: + +* 如果只存在右节点,请验证其存在的证明,并验证它是否是最左侧的节点 +* 如果仅存在左节点,请验证其存在的证据,并验证它是否是最右侧的节点 +* 如果右节点和左节点都存在,请验证它们是否相邻 + +### Substores 到 AppHash 的证明 + +在验证了 IAVL 证明之后,我们就可以开始验证针对 AppHash 的 substore 证明。 首先,迭代 MultiStoreCommitInfo 并通过证明 StoreName 找到 substore commitID。 验证 commitID 中的哈希是否等于证明根哈希,如果不相等则证明无效。 然后通过 substore name 的哈希对 substore commitInfo 数组进行排序。 最后,使用所有 substore commitInfo 数组构建简单的 Merkle 树,并验证 Merkle 根哈希值是否等于appHash。 + +![substore proof](../../../../clients/lite/pics/substoreProof.png) + +```go +func SimpleHashFromTwoHashes(left []byte, right []byte) []byte { + var hasher = ripemd160.New() + + err := encodeByteSlice(hasher, left) + if err != nil { + panic(err) + } + + err = encodeByteSlice(hasher, right) + if err != nil { + panic(err) + } + + return hasher.Sum(nil) +} + +func SimpleHashFromHashes(hashes [][]byte) []byte { + // Recursive impl. + switch len(hashes) { + case 0: + return nil + case 1: + return hashes[0] + default: + left := SimpleHashFromHashes(hashes[:(len(hashes)+1)/2]) + right := SimpleHashFromHashes(hashes[(len(hashes)+1)/2:]) + return SimpleHashFromTwoHashes(left, right) + } +} +``` + +## 根据验证人集验证区块头 + +上面的小节中经常提到 appHash,但可信的appHash来自哪里? 实际上,appHash 存在于区块头中,因此接下来我们需要针对 LCD 可信验证人集验证特定高度的区块头。 验证流程如下所示: + +![commit verification](../../../../clients/lite/pics/commitValidation.png) + +当可信验证人集与区块头不匹配时,我们需要尝试将验证人集更新为此块的高度。 LCD 有一条规则,即每个验证人集的变化不应超过1/3投票权。 如果目标验证人集的投票权变化超过1/3,则与可信验证人集进行比较。 我们必须验证,在目标验证人集之前是否存在隐含的验证人集变更。 只有当所有验证人集变更都遵循这条规则时,才能完成验证人集的更新。 + +例如: + +![Update validator set to height](../../../../clients/lite/pics/updateValidatorToHeight.png) + +* 更新到 10000,失败,变更太大 +* 更新到 5050,失败,变更太大 +* 更新至 2575,成功 +* 更新至 5050,成功 +* 更新到 10000,失败,变更太大 +* 更新至 7525,成功 +* 更新至 10000,成功 \ No newline at end of file diff --git a/docs/translations/cn/clients/service-providers.md b/docs/translations/cn/clients/service-providers.md new file mode 100644 index 0000000000..394e5b6991 --- /dev/null +++ b/docs/translations/cn/clients/service-providers.md @@ -0,0 +1,178 @@ +# 服务提供商(Service Providers) + +我们将“服务提供商”定义为可以为最终用户提供服务的实体,这些实体涉及与基于 Cosmos-SDK 的区块链(包括Cosmos Hub)的某种形式的交互。更具体地说,本文档将集中于与 token 的交互。 + +本节不涉及想要提供[轻客户端](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/light)功能的钱包开发者。服务提供商将作为最终用户的区块链的可信接入点。 + +## 架构的高级描述 + +有三个主要部分需要考虑: + +- 全节点:与区块链交互。 + - Rest Server:它充当 HTTP 调用的中继者。 + - Rest API:为 Rest Server 定义可用端点。 + +##运行全节点 + +###安装和配置 + +我们将描述为 Cosmos Hub 运行和交互全节点的步骤。对于其他基于 SDK 的区块链,该过程是类似的。 + +首先,您需要[安装软件](../cosmos-hub/installation.md). + +然后,您可以开始[运行全节点](../cosmos-hub/join-testnet.md). + +### 命令行界面(CLI) + +接下来,您将用一些 CLI 命令与全节点交互。 + +#### 创建秘钥对 + +生成新秘钥(默认使用 secp256k1椭圆曲线算法): + +```bash +gaiacli keys add +``` + +系统将要求您为此密钥对输入密码(至少8个字符)。该命令返回4个信息: + +- `NAME`: 秘钥名称。 +- `TYPE`:秘钥类型,总是`local`。 +- `ADDRESS`:您的地址,用于接收资金。 +- `PUBKEY`:您的公钥 Your public key. Useful for validators. +- `MNEMONIC`: 由24个单词组成的助记词。 **将这个助记词保存在安全的地方**,它用于在您忘记密码时恢复您的私钥。 + +您可以输入以下命令查看所有可用密钥: + +```bash +gaiacli keys list +``` + +#### 检查您的余额 + +收到代币到您的地址后,您可以输入以下命令查看帐户的余额: + +```bash +gaiacli account +``` + +*注意:当您查询没有 token 帐户的余额时,您将得到以下错误:找不到地址为的帐户。这是预料之中的!我们正在努力改进我们的错误提示信息。* + +#### 通过 CLI 发送代币 + +以下是通过 CLI 发送代币的命令: + +```bash +gaiacli tx send \ + --chain-id= \ + --from= +``` + +参数: +- ``: 接收者地址。 +- ``: 接受``格式的参数,例如 `10faucetToken`。 + +标识: + +- `--chain-id`: This flag allows you to specify the id of the chain. There will be different ids for different testnet chains and main chain. +- `--from`: Name of the key of the sending account. + +#### 帮助 + +如果您需要进行其他操作,最合适的命令是: + +```bash +gaiacli +``` + +它将显示所有可用命令。对于每个命令,您可以使用`--help`标识来获取更多信息。 + +## 设置 Rest 服务器 + +Rest 服务器充当前端点和全节点之间的媒介。 Rest 服务器不必与全节点同一台计算机上运行。 + +要启动 Rest 服务器: + +```bash +gaiacli rest-server --node= +``` + +Flags: +- `--trust-node`: 布尔类型。如果为`true`,轻节点校验将被禁用。如果为`false`, 则会校验返回结果。 对于服务提供商,应将其设置为`true`。默认情况下,它设置为`true`。 +- `--node`: 全节点的IP地址和端口。格式为` `。如果全节点在同一台机器上,则地址应为`tcp:// localhost:26657`。 +- `--laddr`: 此标识允许您指定 Rest 服务器的地址和端口(默认为“1317”)。通常只使用这个标识指定端口,此时只需输入“localhost”作为地址,格式为``。 + + +### 监听入向交易 + +监听入向交易推荐的方法是通过 LCD 的以下端点定期查询区块链: + +[`/bank/balance/{address}`](https://cosmos.network/rpc/#/ICS20/get_bank_balances__address_) + +## Rest API + +Rest API 记录了可用于与全节点交互的所有可用端点,您可以在[这里](https://cosmos.network/rpc/)查看。 + +API 针对每种类别的端点归纳为 ICS 标准。例如,[ICS20](https://cosmos.network/rpc/#/ICS20/)描述了API 与 token 的交互。 + +为了给开发者提供更大的灵活性,我们提供了生成未签名交易、[签名](https://cosmos.network/rpc/#/ICS20/post_tx_sign)和[广播](https://cosmos.network/rpc/#/ICS20/post_tx_broadcast)等不同的 API 端点。这允许服务提供商使用他们自己的签名机制。 + +为了生成一个未签名交易(例如 [coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)),你需要在`base_req`的主体中使用`generate_only`字段。 + +## Cosmos SDK 交易签名 + +Cosmos SDK 签名是一个相当简单的过程。 + +每个 Cosmos SDK 交易都有一个规范的 JSON 描述。 `gaiacli`和 REST 接口为交易提供规范的 JSON 描述,“广播”功能将提供紧凑的 Amino(类似 protobuf 的格式)编码转换。 + +签名消息时的注意事项: + +格式如下 + +```json +{ + "account_number": XXX, + "chain_id": XXX, + "fee": XXX, + "sequence": XXX, + "memo": XXX, + "msgs": XXX +} +``` + +签名者必须提供 `"chain_id"`、 `"account number"` 和 `"sequence number"`。 + +交易构造接口将生成 `"fee"`、 `"msgs"` 和 `"memo"` 等字段. + +You can load the mempool of a full node or validator with a sequence of uncommitted transactions with incrementing +sequence numbers and it will mostly do the correct thing. + +`"account_number"` 和 `"sequence"` 字段可以直接从区块链或本地缓存中查询。 错误的获取了这些数值和chainId,是产生无效签名错误的常见原因。您可以通过加载全节点或验证人中的 mempool 来获取未提交交易的自增序号,这样大大增加成功概率。 + + 您可以使用递增序列号的一系列未提交事务加载完整节点或验证器的mempool,它将主要执行正确的操作。 + +在签名之前,所有键都要按字典顺序排序,并从 JSON 输出中删除所有空格。 + +签名编码是 ECDSArands 的 64字节连结(即`r || s`),其中`s`按字典顺序小于其反转以防止延展性。 这就像以太坊一样,但没有用户公钥恢复的额外字节,因为 Tendermint 假定公钥一定会提供。 + +已签名交易中的签名和公钥示例: + +``` json +{ + "type": "auth/StdTx", + "value": { + "msg": [...], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": XXX + }, + "signature": XXX + } + ], + } +} +``` + +正确生成签名后,将 JSON 插入生成的交易中,然后调用广播端点进行广播。 \ No newline at end of file diff --git a/docs/translations/cn/cn-translation-progress.md b/docs/translations/cn/cn-translation-progress.md new file mode 100644 index 0000000000..6eb3b437e1 --- /dev/null +++ b/docs/translations/cn/cn-translation-progress.md @@ -0,0 +1,42 @@ +# Cosmos SDK Documentation Translation (Chinese) + +This document tracks the progress of the Chinese translation of the official Cosmos SDK documentation. + +Documentation has been translated for **reference use only** and may contain typos, factual errors and be out-of-date with the latest english documentation. + +Please refer to the official english version of the documentation for the latest and accurate information. + +## Cosmos SDK文档翻译 + +本文档跟踪官方 Cosmos SDK 文档的中文翻译进度。 + +已翻译的文件**仅供参考**,可能包含拼写错误、翻译不准确,并且可能慢于英文版更新。 + +有关最新和准确的信息,请参阅文档的官方英文版。 + +## Progress by directory + +### [`concepts`](../concepts/) + +- ToDo + +### [`spec`](../spec/) + +- ToDo + +### [`gaia`](../gaia/) + +- Synced until commit [9e7440a9](https://github.com/cosmos/cosmos-sdk/commit/9e7440a92c14187b05a3064864899f708a507d82) + +### [`intro`](../intro/) + +- Synced until commit [160928b8](https://github.com/cosmos/cosmos-sdk/commit/160928b8f3586a2674cca136891e2cc15d4232d9) + +### [`modules`](../modules/) + +- ToDo + +### [`clients`](../clients/) + +- Synced until Commit [dfe58f8d]() + diff --git a/docs/translations/cn/gaia/installation.md b/docs/translations/cn/gaia/installation.md index 932ae6a52e..6a6214eeba 100644 --- a/docs/translations/cn/gaia/installation.md +++ b/docs/translations/cn/gaia/installation.md @@ -11,6 +11,7 @@ mkdir -p $HOME/go/bin echo "export GOPATH=$HOME/go" >> ~/.bash_profile echo "export GOBIN=\$GOPATH/bin" >> ~/.bash_profile echo "export PATH=\$PATH:\$GOBIN" >> ~/.bash_profile +echo "export GO111MODULE=on" >> ~/.bash_profile source ~/.bash_profile ``` @@ -48,7 +49,7 @@ $ gaiacli version --long ``` cosmos-sdk: 0.33.0 git commit: 7b4104aced52aa5b59a96c28b5ebeea7877fc4f0 -vendor hash: 5db0df3e24cf10545c84f462a24ddc61882aa58f +go.sum hash: d156153bd5e128fec3868eca9a1397a63a864edb5cfa0ac486db1b574b8eecfe build tags: netgo ledger go version go1.12 linux/amd64 ``` @@ -63,4 +64,4 @@ build tags指定了可执行程序具有的特殊特性。 | ledger | 支持Ledger设备(硬件钱包) | ### 接下来 -然后你可以选择 加入公共测试网 或是 创建私有测试网。 \ No newline at end of file +然后你可以选择 加入公共测试网 或是 创建私有测试网。 diff --git a/docs/translations/kr/gaia/installation.md b/docs/translations/kr/gaia/installation.md index 5cfba9dea8..da390e18b5 100755 --- a/docs/translations/kr/gaia/installation.md +++ b/docs/translations/kr/gaia/installation.md @@ -11,6 +11,7 @@ mkdir -p $HOME/go/bin echo "export GOPATH=$HOME/go" >> ~/.bash_profile echo "export GOBIN=\$GOPATH/bin" >> ~/.bash_profile echo "export PATH=\$PATH:\$GOBIN" >> ~/.bash_profile +echo "export GO111MODULE=on" >> ~/.bash_profile source ~/.bash_profile ``` diff --git a/go.mod b/go.mod index a845f33837..cc60848dd9 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/tendermint/btcd v0.1.1 github.com/tendermint/go-amino v0.14.1 github.com/tendermint/iavl v0.12.1 - github.com/tendermint/tendermint v0.31.4 + github.com/tendermint/tendermint v0.31.5 github.com/zondax/hid v0.9.0 // indirect golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 google.golang.org/grpc v1.19.0 // indirect diff --git a/go.sum b/go.sum index f7fe4d2786..3ca7b29b0c 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -146,8 +147,8 @@ github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6o github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/iavl v0.12.1 h1:JDfyhM/Hhrumu1CL1Nxrypm8sNTPYqmeHo1IZLiJoXM= github.com/tendermint/iavl v0.12.1/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM= -github.com/tendermint/tendermint v0.31.4 h1:F/vZ/fMHZJriAkDLjf9ZrReJQsuELiTmJLqigmbE5NU= -github.com/tendermint/tendermint v0.31.4/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc= +github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4= +github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/scripts/install/install_sdk_arm.sh b/scripts/install/install_sdk_arm.sh index ef9e56ece0..d00f5fe16d 100644 --- a/scripts/install/install_sdk_arm.sh +++ b/scripts/install/install_sdk_arm.sh @@ -22,7 +22,7 @@ echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile mkdir go echo "export GOPATH=$HOME/go" >> ~/.profile echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile - +echo "export GO111MODULE=on" >> ~/.profile source ~/.profile # get the code and move into repo @@ -32,7 +32,6 @@ cd $GOPATH/src/$REPO # build & install master git checkout $BRANCH LEDGER_ENABLED=false make tools -LEDGER_ENABLED=false make get_vendor_deps LEDGER_ENABLED=false make install source ~/.profile diff --git a/scripts/install/install_sdk_bsd.sh b/scripts/install/install_sdk_bsd.sh index 4784addc3e..1db4062511 100644 --- a/scripts/install/install_sdk_bsd.sh +++ b/scripts/install/install_sdk_bsd.sh @@ -48,6 +48,5 @@ cd $GOPATH/src/$REPO # build & install master git checkout $BRANCH gmake tools -gmake get_vendor_deps gmake install gmake install_examples diff --git a/scripts/install/install_sdk_ubuntu.sh b/scripts/install/install_sdk_ubuntu.sh index 079eed8312..2f62fe4156 100644 --- a/scripts/install/install_sdk_ubuntu.sh +++ b/scripts/install/install_sdk_ubuntu.sh @@ -25,7 +25,7 @@ echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile mkdir go echo "export GOPATH=$HOME/go" >> ~/.profile echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile - +echo "export GO111MODULE=on" >> ~/.profile source ~/.profile # get the code and move into repo @@ -35,7 +35,6 @@ cd $GOPATH/src/$REPO # build & install master git checkout $BRANCH LEDGER_ENABLED=false make tools -LEDGER_ENABLED=false make get_vendor_deps LEDGER_ENABLED=false make install source ~/.profile diff --git a/server/config/config.go b/server/config/config.go index 11ff534885..1507999345 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -17,6 +17,10 @@ type BaseConfig struct { // transaction. A transaction's fees must meet the minimum of any denomination // specified in this config (e.g. 0.25token1;0.0001token2). MinGasPrices string `mapstructure:"minimum-gas-prices"` + + // HaltHeight contains a non-zero height at which a node will gracefully halt + // and shutdown that can be used to assist upgrades and testing. + HaltHeight uint64 `mapstructure:"halt-height"` } // Config defines the server's top level configuration @@ -56,6 +60,7 @@ func DefaultConfig() *Config { return &Config{ BaseConfig{ MinGasPrices: defaultMinGasPrices, + HaltHeight: 0, }, } } diff --git a/server/config/toml.go b/server/config/toml.go index 31e6ea9bcf..7f585b981f 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -17,6 +17,10 @@ const defaultConfigTemplate = `# This is a TOML config file. # transaction. A transaction's fees must meet the minimum of any denomination # specified in this config (e.g. 0.25token1;0.0001token2). minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}" + +# HaltHeight contains a non-zero height at which a node will gracefully halt +# and shutdown that can be used to assist upgrades and testing. +halt-height = {{ .BaseConfig.HaltHeight }} ` var configTemplate *template.Template diff --git a/server/constructors.go b/server/constructors.go index ac087d6577..939bba9548 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -6,11 +6,12 @@ import ( "os" "path/filepath" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" ) type ( diff --git a/server/start.go b/server/start.go index 4b976c822b..189a2c8668 100644 --- a/server/start.go +++ b/server/start.go @@ -23,6 +23,7 @@ const ( flagTraceStore = "trace-store" flagPruning = "pruning" FlagMinGasPrices = "minimum-gas-prices" + FlagHaltHeight = "halt-height" ) // StartCmd runs the service passed in, either stand-alone or in-process with @@ -53,6 +54,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)", ) + cmd.Flags().Uint64(FlagHaltHeight, 0, "Height at which to gracefully halt the chain and shutdown the node") // add support for all Tendermint-specific command line options tcmd.AddNodeFlags(cmd) diff --git a/server/util.go b/server/util.go index 2b81ce817a..6fe6cce516 100644 --- a/server/util.go +++ b/server/util.go @@ -105,7 +105,10 @@ func interceptLoadConfig() (conf *cfg.Config, err error) { conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. } - // create a default gaia config file if it does not exist + // create a default Gaia config file if it does not exist + // + // TODO: Rename config file to server.toml as it's not particular to Gaia + // (REF: https://github.com/cosmos/cosmos-sdk/issues/4125). gaiaConfigFilePath := filepath.Join(rootDir, "config/gaiad.toml") if _, err := os.Stat(gaiaConfigFilePath); os.IsNotExist(err) { gaiaConf, _ := config.ParseConfig() diff --git a/types/address.go b/types/address.go index 05a18ecab7..d62f947af1 100644 --- a/types/address.go +++ b/types/address.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/encoding/amino" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/libs/bech32" ) diff --git a/types/address_test.go b/types/address_test.go index 129f002a1b..754d3e754c 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -7,9 +7,10 @@ import ( "strings" - "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/cosmos/cosmos-sdk/types" ) var invalidStrs = []string{ diff --git a/types/invariant.go b/types/invariant.go index 86c9024faa..aa8aca5665 100644 --- a/types/invariant.go +++ b/types/invariant.go @@ -6,5 +6,5 @@ package types // The simulator will then halt and print the logs. type Invariant func(ctx Context) error -// group of Invarient +// Invariants defines a group of invariants type Invariants []Invariant diff --git a/types/rest/rest.go b/types/rest/rest.go index 20fcef3fc7..71549584da 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -5,13 +5,14 @@ package rest import ( "errors" "fmt" - "github.com/tendermint/tendermint/types" "io/ioutil" "net/http" "net/url" "strconv" "strings" + "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/types/tags.go b/types/tags.go index a6726200e5..c203f53ac7 100644 --- a/types/tags.go +++ b/types/tags.go @@ -71,6 +71,8 @@ func MakeTag(k string, v string) Tag { // common tags var ( TagAction = "action" + TagCategory = "category" + TagSender = "sender" TagSrcValidator = "source-validator" TagDstValidator = "destination-validator" TagDelegator = "delegator" diff --git a/version/version.go b/version/version.go index 0b2c09985a..aad02d1904 100644 --- a/version/version.go +++ b/version/version.go @@ -8,33 +8,33 @@ import ( // Variables set by build flags var ( - Commit = "" - Version = "" - VendorDirHash = "" - BuildTags = "" + Commit = "" + Version = "" + GoSumHash = "" + BuildTags = "" ) type versionInfo struct { - CosmosSDK string `json:"cosmos_sdk"` - GitCommit string `json:"commit"` - VendorDirHash string `json:"vendor_hash"` - BuildTags string `json:"build_tags"` - GoVersion string `json:"go"` + CosmosSDK string `json:"cosmos_sdk"` + GitCommit string `json:"commit"` + GoSumHash string `json:"gosum_hash"` + BuildTags string `json:"build_tags"` + GoVersion string `json:"go"` } func (v versionInfo) String() string { return fmt.Sprintf(`cosmos-sdk: %s git commit: %s -vendor hash: %s +go.sum hash: %s build tags: %s -%s`, v.CosmosSDK, v.GitCommit, v.VendorDirHash, v.BuildTags, v.GoVersion) +%s`, v.CosmosSDK, v.GitCommit, v.GoSumHash, v.BuildTags, v.GoVersion) } func newVersionInfo() versionInfo { return versionInfo{ Version, Commit, - VendorDirHash, + GoSumHash, BuildTags, fmt.Sprintf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)} } diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index e7345f3d45..9df3c14bc0 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -47,9 +47,9 @@ func QueryAccountRequestHandlerFn( return } - // the query will return empty if there is no data for this account + // the query will return empty account if there is no data if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) + rest.PostProcessResponse(w, cdc, auth.BaseAccount{}, cliCtx.Indent) return } @@ -88,7 +88,7 @@ func QueryBalancesRequestHandlerFn( // the query will return empty if there is no data for this account if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) + rest.PostProcessResponse(w, cdc, sdk.Coins{}, cliCtx.Indent) return } diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 034ef86f8d..fe9225eef0 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -178,8 +178,7 @@ func (bldr TxBuilder) WithAccountNumber(accnum uint64) TxBuilder { // set of messages. It returns an error if a fee is supplied but cannot be // parsed. func (bldr TxBuilder) BuildSignMsg(msgs []sdk.Msg) (StdSignMsg, error) { - chainID := bldr.chainID - if chainID == "" { + if bldr.chainID == "" { return StdSignMsg{}, fmt.Errorf("chain ID required but not specified") } diff --git a/x/auth/client/txbuilder/txbuilder_test.go b/x/auth/client/txbuilder/txbuilder_test.go index 3ae2f71242..4ff4b0cec5 100644 --- a/x/auth/client/txbuilder/txbuilder_test.go +++ b/x/auth/client/txbuilder/txbuilder_test.go @@ -33,12 +33,14 @@ func TestTxBuilderBuild(t *testing.T) { } defaultMsg := []sdk.Msg{sdk.NewTestMsg(addr)} tests := []struct { + name string fields fields msgs []sdk.Msg want StdSignMsg wantErr bool }{ { + "builder with fees", fields{ TxEncoder: auth.DefaultTxEncoder(codec.New()), AccountNumber: 1, @@ -48,7 +50,7 @@ func TestTxBuilderBuild(t *testing.T) { SimulateGas: false, ChainID: "test-chain", Memo: "hello from Voyager 1!", - Fees: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}, + Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), }, defaultMsg, StdSignMsg{ @@ -62,6 +64,7 @@ func TestTxBuilderBuild(t *testing.T) { false, }, { + "builder with gas prices", fields{ TxEncoder: auth.DefaultTxEncoder(codec.New()), AccountNumber: 1, @@ -84,19 +87,69 @@ func TestTxBuilderBuild(t *testing.T) { }, false, }, + { + "no chain-id supplied", + fields{ + TxEncoder: auth.DefaultTxEncoder(codec.New()), + AccountNumber: 1, + Sequence: 1, + Gas: 200000, + GasAdjustment: 1.1, + SimulateGas: false, + ChainID: "", + Memo: "hello from Voyager 1!", + Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), + }, + defaultMsg, + StdSignMsg{ + ChainID: "test-chain", + AccountNumber: 1, + Sequence: 1, + Memo: "hello from Voyager 1!", + Msgs: defaultMsg, + Fee: auth.NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), + }, + true, + }, + { + "builder w/ fees and gas prices", + fields{ + TxEncoder: auth.DefaultTxEncoder(codec.New()), + AccountNumber: 1, + Sequence: 1, + Gas: 200000, + GasAdjustment: 1.1, + SimulateGas: false, + ChainID: "test-chain", + Memo: "hello from Voyager 1!", + Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), + GasPrices: sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, + }, + defaultMsg, + StdSignMsg{ + ChainID: "test-chain", + AccountNumber: 1, + Sequence: 1, + Memo: "hello from Voyager 1!", + Msgs: defaultMsg, + Fee: auth.NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), + }, + true, + }, } - for i, tc := range tests { - bldr := NewTxBuilder( - tc.fields.TxEncoder, tc.fields.AccountNumber, tc.fields.Sequence, - tc.fields.Gas, tc.fields.GasAdjustment, tc.fields.SimulateGas, - tc.fields.ChainID, tc.fields.Memo, tc.fields.Fees, tc.fields.GasPrices, - ) - - got, err := bldr.BuildSignMsg(tc.msgs) - require.Equal(t, tc.wantErr, (err != nil), "TxBuilder.Build() error = %v, wantErr %v, tc %d", err, tc.wantErr, i) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("TxBuilder.Build() = %v, want %v", got, tc.want) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bldr := NewTxBuilder( + tt.fields.TxEncoder, tt.fields.AccountNumber, tt.fields.Sequence, + tt.fields.Gas, tt.fields.GasAdjustment, tt.fields.SimulateGas, + tt.fields.ChainID, tt.fields.Memo, tt.fields.Fees, tt.fields.GasPrices, + ) + got, err := bldr.BuildSignMsg(tt.msgs) + require.Equal(t, tt.wantErr, (err != nil)) + if err == nil { + require.True(t, reflect.DeepEqual(tt.want, got)) + } + }) } } diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 2f1d45b95a..9225ae2785 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -1,8 +1,6 @@ package cli import ( - "fmt" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -22,12 +20,12 @@ const ( // SendTxCmd will create a send tx and sign it with the given key. func SendTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "send [to_address] [amount]", + Use: "send [from_key_or_address] [to_address] [amount]", Short: "Create and sign a send tx", - Args: cobra.ExactArgs(2), + Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). + cliCtx := context.NewCLIContextWithFrom(args[0]). WithCodec(cdc). WithAccountDecoder(cdc) @@ -35,36 +33,24 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { return err } - to, err := sdk.AccAddressFromBech32(args[0]) + to, err := sdk.AccAddressFromBech32(args[1]) if err != nil { return err } // parse coins trying to be sent - coins, err := sdk.ParseCoins(args[1]) + coins, err := sdk.ParseCoins(args[2]) if err != nil { return err } - from := cliCtx.GetFromAddress() - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - - // ensure account has enough coins - if !account.GetCoins().IsAllGTE(coins) { - return fmt.Errorf("address %s doesn't have enough coins to pay for this transaction", from) - } - // build and sign the transaction, then broadcast to Tendermint - msg := bank.NewMsgSend(from, to, coins) + msg := bank.NewMsgSend(cliCtx.GetFromAddress(), to, coins) return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false) }, } cmd = client.PostCommands(cmd)[0] - cmd.MarkFlagRequired(client.FlagFrom) return cmd } diff --git a/x/bank/errors.go b/x/bank/errors.go index e76debe303..7c5feaf4a8 100644 --- a/x/bank/errors.go +++ b/x/bank/errors.go @@ -14,7 +14,7 @@ const ( // ErrNoInputs is an error func ErrNoInputs(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidInputsOutputs, "no inputs to send transacction") + return sdk.NewError(codespace, CodeInvalidInputsOutputs, "no inputs to send transaction") } // ErrNoOutputs is an error diff --git a/x/bank/handler.go b/x/bank/handler.go index e587381351..3afff836ae 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -1,7 +1,10 @@ package bank import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/tags" ) // NewHandler returns a handler for "bank" type messages. @@ -10,10 +13,12 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgSend: return handleMsgSend(ctx, k, msg) + case MsgMultiSend: return handleMsgMultiSend(ctx, k, msg) + default: - errMsg := "Unrecognized bank Msg type: %s" + msg.Type() + errMsg := fmt.Sprintf("unrecognized bank message type: %T", msg) return sdk.ErrUnknownRequest(errMsg).Result() } } @@ -24,13 +29,19 @@ func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { if !k.GetSendEnabled(ctx) { return ErrSendDisabled(k.Codespace()).Result() } - tags, err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount) + err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount) if err != nil { return err.Result() } + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.FromAddress.String(), + tags.Recipient, msg.ToAddress.String(), + ) + return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -40,12 +51,13 @@ func handleMsgMultiSend(ctx sdk.Context, k Keeper, msg MsgMultiSend) sdk.Result if !k.GetSendEnabled(ctx) { return ErrSendDisabled(k.Codespace()).Result() } - tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) + resTags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) if err != nil { return err.Result() } + resTags = resTags.AppendTag(tags.Category, tags.TxCategory) return sdk.Result{ - Tags: tags, + Tags: resTags, } } diff --git a/x/bank/handler_test.go b/x/bank/handler_test.go new file mode 100644 index 0000000000..69021d30b4 --- /dev/null +++ b/x/bank/handler_test.go @@ -0,0 +1,18 @@ +package bank + +import ( + "strings" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/stretchr/testify/require" +) + +func TestInvalidMsg(t *testing.T) { + h := NewHandler(nil) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized bank message type")) +} diff --git a/x/bank/keeper.go b/x/bank/keeper.go index c5322ec5c7..05a5b3d6e3 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank/tags" "github.com/cosmos/cosmos-sdk/x/params" ) @@ -17,8 +18,8 @@ type Keeper interface { SendKeeper SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) DelegateCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) @@ -60,10 +61,10 @@ func (keeper BaseKeeper) SetCoins( // SubtractCoins subtracts amt from the coins at the addr. func (keeper BaseKeeper) SubtractCoins( ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, -) (sdk.Coins, sdk.Tags, sdk.Error) { +) (sdk.Coins, sdk.Error) { if !amt.IsValid() { - return nil, nil, sdk.ErrInvalidCoins(amt.String()) + return nil, sdk.ErrInvalidCoins(amt.String()) } return subtractCoins(ctx, keeper.ak, addr, amt) } @@ -71,10 +72,10 @@ func (keeper BaseKeeper) SubtractCoins( // AddCoins adds amt to the coins at the addr. func (keeper BaseKeeper) AddCoins( ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, -) (sdk.Coins, sdk.Tags, sdk.Error) { +) (sdk.Coins, sdk.Error) { if !amt.IsValid() { - return nil, nil, sdk.ErrInvalidCoins(amt.String()) + return nil, sdk.ErrInvalidCoins(amt.String()) } return addCoins(ctx, keeper.ak, addr, amt) } @@ -119,7 +120,7 @@ func (keeper BaseKeeper) UndelegateCoins( type SendKeeper interface { ViewKeeper - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error GetSendEnabled(ctx sdk.Context) bool SetSendEnabled(ctx sdk.Context, enabled bool) @@ -150,10 +151,10 @@ func NewBaseSendKeeper(ak auth.AccountKeeper, // SendCoins moves coins from one account to another func (keeper BaseSendKeeper) SendCoins( ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins, -) (sdk.Tags, sdk.Error) { +) sdk.Error { if !amt.IsValid() { - return nil, sdk.ErrInvalidCoins(amt.String()) + return sdk.ErrInvalidCoins(amt.String()) } return sendCoins(ctx, keeper.ak, fromAddr, toAddr, amt) } @@ -249,10 +250,10 @@ func setAccount(ctx sdk.Context, ak auth.AccountKeeper, acc auth.Account) { // subtractCoins subtracts amt coins from an account with the given address addr. // // CONTRACT: If the account is a vesting account, the amount has to be spendable. -func subtractCoins(ctx sdk.Context, ak auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { +func subtractCoins(ctx sdk.Context, ak auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) { if !amt.IsValid() { - return nil, nil, sdk.ErrInvalidCoins(amt.String()) + return nil, sdk.ErrInvalidCoins(amt.String()) } oldCoins, spendableCoins := sdk.NewCoins(), sdk.NewCoins() @@ -267,59 +268,57 @@ func subtractCoins(ctx sdk.Context, ak auth.AccountKeeper, addr sdk.AccAddress, // So the check here is sufficient instead of subtracting from oldCoins. _, hasNeg := spendableCoins.SafeSub(amt) if hasNeg { - return amt, nil, sdk.ErrInsufficientCoins( + return amt, sdk.ErrInsufficientCoins( fmt.Sprintf("insufficient account funds; %s < %s", spendableCoins, amt), ) } newCoins := oldCoins.Sub(amt) // should not panic as spendable coins was already checked err := setCoins(ctx, ak, addr, newCoins) - tags := sdk.NewTags(TagKeySender, addr.String()) - return newCoins, tags, err + return newCoins, err } // AddCoins adds amt to the coins at the addr. -func addCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { +func addCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) { if !amt.IsValid() { - return nil, nil, sdk.ErrInvalidCoins(amt.String()) + return nil, sdk.ErrInvalidCoins(amt.String()) } oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Add(amt) if newCoins.IsAnyNegative() { - return amt, nil, sdk.ErrInsufficientCoins( + return amt, sdk.ErrInsufficientCoins( fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt), ) } err := setCoins(ctx, am, addr, newCoins) - tags := sdk.NewTags(TagKeyRecipient, addr.String()) - return newCoins, tags, err + return newCoins, err } // SendCoins moves coins from one account to another // Returns ErrInvalidCoins if amt is invalid. -func sendCoins(ctx sdk.Context, am auth.AccountKeeper, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) { +func sendCoins(ctx sdk.Context, am auth.AccountKeeper, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error { // Safety check ensuring that when sending coins the keeper must maintain the if !amt.IsValid() { - return nil, sdk.ErrInvalidCoins(amt.String()) + return sdk.ErrInvalidCoins(amt.String()) } - _, subTags, err := subtractCoins(ctx, am, fromAddr, amt) + _, err := subtractCoins(ctx, am, fromAddr, amt) if err != nil { - return nil, err + return err } - _, addTags, err := addCoins(ctx, am, toAddr, amt) + _, err = addCoins(ctx, am, toAddr, amt) if err != nil { - return nil, err + return err } - return subTags.AppendTags(addTags), nil + return nil } // InputOutputCoins handles a list of inputs and outputs @@ -334,19 +333,24 @@ func inputOutputCoins(ctx sdk.Context, am auth.AccountKeeper, inputs []Input, ou allTags := sdk.EmptyTags() for _, in := range inputs { - _, tags, err := subtractCoins(ctx, am, in.Address, in.Coins) + _, err := subtractCoins(ctx, am, in.Address, in.Coins) if err != nil { return nil, err } - allTags = allTags.AppendTags(tags) + + allTags = allTags.AppendTag( + tags.Sender, in.Address.String(), + ) } for _, out := range outputs { - _, tags, err := addCoins(ctx, am, out.Address, out.Coins) + _, err := addCoins(ctx, am, out.Address, out.Coins) if err != nil { return nil, err } - allTags = allTags.AppendTags(tags) + allTags = allTags.AppendTag( + tags.Recipient, out.Address.String(), + ) } return allTags, nil @@ -381,8 +385,8 @@ func delegateCoins( setAccount(ctx, ak, acc) return sdk.NewTags( - sdk.TagAction, TagActionDelegateCoins, - sdk.TagDelegator, []byte(addr.String()), + sdk.TagAction, tags.ActionDelegateCoins, + sdk.TagDelegator, addr.String(), ), nil } @@ -406,8 +410,8 @@ func undelegateCoins( setAccount(ctx, ak, acc) return sdk.NewTags( - sdk.TagAction, TagActionUndelegateCoins, - sdk.TagDelegator, []byte(addr.String()), + sdk.TagAction, tags.ActionUndelegateCoins, + sdk.TagDelegator, addr.String(), ), nil } diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index ee8e7e6556..28db4a3dcc 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -101,7 +101,7 @@ func TestKeeper(t *testing.T) { require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - _, err2 := bankKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) + err2 := bankKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) require.Implements(t, (*sdk.Error)(nil), err2) require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) @@ -165,7 +165,7 @@ func TestSendKeeper(t *testing.T) { require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) require.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - _, err := sendKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) + err := sendKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) require.Implements(t, (*sdk.Error)(nil), err) require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) require.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) @@ -178,7 +178,7 @@ func TestSendKeeper(t *testing.T) { // validate coins with invalid denoms or negative values cannot be sent // NOTE: We must use the Coin literal as the constructor does not allow // negative values. - _, err = sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{sdk.Coin{"FOOCOIN", sdk.NewInt(-5)}}) + err = sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{sdk.Coin{"FOOCOIN", sdk.NewInt(-5)}}) require.Error(t, err) } @@ -226,7 +226,7 @@ func TestVestingAccountSend(t *testing.T) { input.ak.SetAccount(ctx, vacc) // require that no coins be sendable at the beginning of the vesting schedule - _, err := bankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) + err := bankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) require.Error(t, err) // receive some coins @@ -235,7 +235,7 @@ func TestVestingAccountSend(t *testing.T) { // require that all vested coins are spendable plus any received ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) - _, err = bankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) + err = bankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) vacc = input.ak.GetAccount(ctx, addr1).(*auth.ContinuousVestingAccount) require.NoError(t, err) require.Equal(t, origCoins, vacc.GetCoins()) diff --git a/x/bank/tags.go b/x/bank/tags.go deleted file mode 100644 index 264d893097..0000000000 --- a/x/bank/tags.go +++ /dev/null @@ -1,10 +0,0 @@ -package bank - -// Tag keys and values -var ( - TagActionUndelegateCoins = []byte("undelegateCoins") - TagActionDelegateCoins = []byte("delegateCoins") - - TagKeyRecipient = "recipient" - TagKeySender = "sender" -) diff --git a/x/bank/tags/tags.go b/x/bank/tags/tags.go new file mode 100644 index 0000000000..b458681833 --- /dev/null +++ b/x/bank/tags/tags.go @@ -0,0 +1,17 @@ +package tags + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Tag keys and values +var ( + ActionUndelegateCoins = "undelegateCoins" + ActionDelegateCoins = "delegateCoins" + TxCategory = "bank" + + Action = sdk.TagAction + Category = sdk.TagCategory + Recipient = "recipient" + Sender = "sender" +) diff --git a/x/crisis/expected_keepers.go b/x/crisis/expected_keepers.go index e620bdd3aa..9e505ca4c7 100644 --- a/x/crisis/expected_keepers.go +++ b/x/crisis/expected_keepers.go @@ -16,5 +16,5 @@ type FeeCollectionKeeper interface { // expected bank keeper type BankKeeper interface { - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) } diff --git a/x/crisis/handler.go b/x/crisis/handler.go index ae753804d1..da30d976aa 100644 --- a/x/crisis/handler.go +++ b/x/crisis/handler.go @@ -1,7 +1,10 @@ package crisis import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/tags" ) // ModuleName is the module name for this module @@ -12,12 +15,13 @@ const ( func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { case MsgVerifyInvariant: return handleMsgVerifyInvariant(ctx, msg, k) + default: - return sdk.ErrTxDecode("invalid message parse in crisis module").Result() + errMsg := fmt.Sprintf("unrecognized crisis message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() } } } @@ -26,7 +30,7 @@ func handleMsgVerifyInvariant(ctx sdk.Context, msg MsgVerifyInvariant, k Keeper) // remove the constant fee constantFee := sdk.NewCoins(k.GetConstantFee(ctx)) - _, _, err := k.bankKeeper.SubtractCoins(ctx, msg.Sender, constantFee) + _, err := k.bankKeeper.SubtractCoins(ctx, msg.Sender, constantFee) if err != nil { return err.Result() } @@ -70,11 +74,11 @@ func handleMsgVerifyInvariant(ctx sdk.Context, msg MsgVerifyInvariant, k Keeper) panic(invarianceErr) } - tags := sdk.NewTags( - "sender", msg.Sender.String(), - "invariant", msg.InvariantRoute, + resTags := sdk.NewTags( + tags.Sender, msg.Sender.String(), + tags.Invariant, msg.InvariantRoute, ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } diff --git a/x/crisis/handler_test.go b/x/crisis/handler_test.go index 1f0aa6d147..acc920e4a1 100644 --- a/x/crisis/handler_test.go +++ b/x/crisis/handler_test.go @@ -3,12 +3,14 @@ package crisis import ( "errors" "fmt" + "strings" "testing" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/stretchr/testify/require" ) var ( @@ -98,3 +100,12 @@ func TestHandleMsgVerifyInvariantWithInvariantNotBroken(t *testing.T) { res := handleMsgVerifyInvariant(ctx, msg, crisisKeeper) require.True(t, res.IsOK()) } + +func TestInvalidMsg(t *testing.T) { + k := Keeper{} + h := NewHandler(k) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized crisis message type")) +} diff --git a/x/crisis/keeper.go b/x/crisis/keeper.go index 8501b08ee0..95c7cf09c1 100644 --- a/x/crisis/keeper.go +++ b/x/crisis/keeper.go @@ -39,3 +39,14 @@ func (k *Keeper) RegisterRoute(moduleName, route string, invar sdk.Invariant) { func (k Keeper) Routes() []InvarRoute { return k.routes } + +// Invariants returns all the registered Crisis keeper invariants. +func (k Keeper) Invariants() []sdk.Invariant { + var invars []sdk.Invariant + for _, route := range k.routes { + invars = append(invars, route.Invar) + } + return invars +} + +// DONTCOVER diff --git a/x/crisis/tags/tags.go b/x/crisis/tags/tags.go new file mode 100644 index 0000000000..da3c667936 --- /dev/null +++ b/x/crisis/tags/tags.go @@ -0,0 +1,11 @@ +package tags + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Crisis module tags +var ( + Sender = sdk.TagSender + Invariant = "invariant" +) diff --git a/x/distribution/alias.go b/x/distribution/alias.go index 616991a47a..57038618bc 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -27,6 +27,10 @@ type ( QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams QueryDelegatorWithdrawAddrParams = keeper.QueryDelegatorWithdrawAddrParams + + // querier response types + QueryDelegatorTotalRewardsResponse = types.QueryDelegatorTotalRewardsResponse + DelegationDelegatorReward = types.DelegationDelegatorReward ) const ( @@ -44,7 +48,6 @@ var ( ErrNilValidatorAddr = types.ErrNilValidatorAddr TagValidator = tags.Validator - TagDelegator = tags.Delegator NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward @@ -72,4 +75,21 @@ var ( DefaultGenesisState = types.DefaultGenesisState ValidateGenesis = types.ValidateGenesis InitialFeePool = types.InitialFeePool + + // Query types + QueryParams = keeper.QueryParams + QueryValidatorOutstandingRewards = keeper.QueryValidatorOutstandingRewards + QueryValidatorCommission = keeper.QueryValidatorCommission + QueryValidatorSlashes = keeper.QueryValidatorSlashes + QueryDelegationRewards = keeper.QueryDelegationRewards + QueryDelegatorTotalRewards = keeper.QueryDelegatorTotalRewards + QueryDelegatorValidators = keeper.QueryDelegatorValidators + QueryWithdrawAddr = keeper.QueryWithdrawAddr + QueryCommunityPool = keeper.QueryCommunityPool + + // Param types + ParamCommunityTax = keeper.ParamCommunityTax + ParamBaseProposerReward = keeper.ParamBaseProposerReward + ParamBonusProposerReward = keeper.ParamBonusProposerReward + ParamWithdrawAddrEnabled = keeper.ParamWithdrawAddrEnabled ) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 7c4c2bcbd7..bc5777ac07 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -144,20 +144,25 @@ $ gaiacli query distr rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosm RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - var resp []byte - var err error if len(args) == 2 { // query for rewards from a particular delegation - resp, err = common.QueryDelegationRewards(cliCtx, cdc, queryRoute, args[0], args[1]) - } else { - // query for delegator total rewards - resp, err = common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, args[0]) + resp, err := common.QueryDelegationRewards(cliCtx, cdc, queryRoute, args[0], args[1]) + if err != nil { + return err + } + + var result sdk.DecCoins + cdc.MustUnmarshalJSON(resp, &result) + return cliCtx.PrintOutput(result) } + + // query for delegator total rewards + resp, err := common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, args[0]) if err != nil { return err } - var result sdk.DecCoins + var result distr.QueryDelegatorTotalRewardsResponse cdc.MustUnmarshalJSON(resp, &result) return cliCtx.PrintOutput(result) }, diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index de78a9bf23..2f2f6b38b6 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -11,26 +11,26 @@ import ( // QueryParams actually queries distribution params. func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { - route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) + route := fmt.Sprintf("custom/%s/params/%s", queryRoute, distr.ParamCommunityTax) retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) if err != nil { return PrettyParams{}, err } - route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) + route = fmt.Sprintf("custom/%s/params/%s", queryRoute, distr.ParamBaseProposerReward) retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) if err != nil { return PrettyParams{}, err } - route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) + route = fmt.Sprintf("custom/%s/params/%s", queryRoute, distr.ParamBonusProposerReward) retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) if err != nil { return PrettyParams{}, err } - route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) + route = fmt.Sprintf("custom/%s/params/%s", queryRoute, distr.ParamWithdrawAddrEnabled) retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) if err != nil { return PrettyParams{}, err @@ -50,7 +50,7 @@ func QueryDelegatorTotalRewards(cliCtx context.CLIContext, cdc *codec.Codec, } return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute), + fmt.Sprintf("custom/%s/%s", queryRoute, distr.QueryDelegatorTotalRewards), cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), ) } @@ -69,7 +69,7 @@ func QueryDelegationRewards(cliCtx context.CLIContext, cdc *codec.Codec, } return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegation_rewards", queryRoute), + fmt.Sprintf("custom/%s/%s", queryRoute, distr.QueryDelegationRewards), cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), ) } @@ -80,7 +80,7 @@ func QueryDelegatorValidators(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegator_validators", queryRoute), + fmt.Sprintf("custom/%s/%s", queryRoute, distr.QueryDelegatorValidators), cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), ) } @@ -90,7 +90,7 @@ func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/validator_commission", queryRoute), + fmt.Sprintf("custom/%s/%s", queryRoute, distr.QueryValidatorCommission), cdc.MustMarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)), ) } diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 081baa869f..64f824b9f8 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -1,6 +1,8 @@ package distribution import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/tags" @@ -13,12 +15,16 @@ func NewHandler(k keeper.Keeper) sdk.Handler { switch msg := msg.(type) { case types.MsgSetWithdrawAddress: return handleMsgModifyWithdrawAddress(ctx, msg, k) + case types.MsgWithdrawDelegatorReward: return handleMsgWithdrawDelegatorReward(ctx, msg, k) + case types.MsgWithdrawValidatorCommission: return handleMsgWithdrawValidatorCommission(ctx, msg, k) + default: - return sdk.ErrTxDecode("invalid message parse in distribution module").Result() + errMsg := fmt.Sprintf("unrecognized distribution message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() } } } @@ -32,11 +38,12 @@ func handleMsgModifyWithdrawAddress(ctx sdk.Context, msg types.MsgSetWithdrawAdd return err.Result() } - tags := sdk.NewTags( - tags.Delegator, []byte(msg.DelegatorAddress.String()), + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -47,12 +54,13 @@ func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDele return err.Result() } - tags := sdk.NewTags( - tags.Delegator, []byte(msg.DelegatorAddress.String()), - tags.Validator, []byte(msg.ValidatorAddress.String()), + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), + tags.Validator, msg.ValidatorAddress.String(), ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -63,10 +71,11 @@ func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdraw return err.Result() } - tags := sdk.NewTags( - tags.Validator, []byte(msg.ValidatorAddress.String()), + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.ValidatorAddress.String(), ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } diff --git a/x/distribution/keeper/allocation.go b/x/distribution/keeper/allocation.go index df5be3b923..abc2575b2c 100644 --- a/x/distribution/keeper/allocation.go +++ b/x/distribution/keeper/allocation.go @@ -12,7 +12,7 @@ import ( func (k Keeper) AllocateTokens(ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64, previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) { - logger := ctx.Logger().With("module", "x/distribution") + logger := k.Logger(ctx) // fetch and clear the collected fees for distribution, since this is // called in BeginBlock, collected fees will be from the previous block diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 677c38342c..20321e870a 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -131,7 +131,7 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val sdk.Validator, de // of the decCoins due to operation order of the distribution mechanism. rewards := rewardsRaw.Intersect(outstanding) if !rewards.IsEqual(rewardsRaw) { - logger := ctx.Logger().With("module", "x/distr") + logger := k.Logger(ctx) logger.Info(fmt.Sprintf("missing rewards rounding error, delegator %v"+ "withdrawing rewards from validator %v, should have received %v, got %v", val.GetOperator(), del.GetDelegatorAddr(), rewardsRaw, rewards)) @@ -153,7 +153,7 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val sdk.Validator, de // add coins to user account if !coins.IsZero() { withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr()) - if _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { + if _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { return err } } diff --git a/x/distribution/keeper/fee_pool.go b/x/distribution/keeper/fee_pool.go index d8e6998db5..779a58d139 100644 --- a/x/distribution/keeper/fee_pool.go +++ b/x/distribution/keeper/fee_pool.go @@ -15,7 +15,7 @@ func (k Keeper) DistributeFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr } feePool.CommunityPool.Sub(sdk.NewDecCoins(amount)) - _, _, err := k.bankKeeper.AddCoins(ctx, receiveAddr, amount) + _, err := k.bankKeeper.AddCoins(ctx, receiveAddr, amount) if err != nil { return err } diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 1ce7a1fd63..022c8d943c 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -46,7 +46,7 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr accAddr := sdk.AccAddress(valAddr) withdrawAddr := h.k.GetDelegatorWithdrawAddr(ctx, accAddr) - if _, _, err := h.k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { + if _, err := h.k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { panic(err) } } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 5741893aa3..1a4c049282 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -5,6 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/tendermint/tendermint/libs/log" ) // keeper of the staking store @@ -35,6 +37,9 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, c return keeper } +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/distr") } + // set withdraw address func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) sdk.Error { if !k.GetWithdrawAddrEnabled(ctx) { @@ -89,7 +94,7 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr accAddr := sdk.AccAddress(valAddr) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr) - if _, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { + if _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, coins); err != nil { return err } } diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 984bb74482..6c172b8b79 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -1,6 +1,7 @@ package keeper import ( + "encoding/json" "fmt" abci "github.com/tendermint/tendermint/abci/types" @@ -251,21 +252,25 @@ func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQue // cache-wrap context as to not persist state changes during querying ctx, _ = ctx.CacheContext() - var totalRewards sdk.DecCoins + total := sdk.DecCoins{} + var delRewards []types.DelegationDelegatorReward k.stakingKeeper.IterateDelegations( ctx, params.DelegatorAddress, func(_ int64, del sdk.Delegation) (stop bool) { - val := k.stakingKeeper.Validator(ctx, del.GetValidatorAddr()) + valAddr := del.GetValidatorAddr() + val := k.stakingKeeper.Validator(ctx, valAddr) endingPeriod := k.incrementValidatorPeriod(ctx, val) - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + delReward := k.calculateDelegationRewards(ctx, val, del, endingPeriod) - totalRewards = totalRewards.Add(rewards) + delRewards = append(delRewards, types.NewDelegationDelegatorReward(valAddr, delReward)) + total = total.Add(delReward) return false }, ) - bz, err := codec.MarshalJSONIndent(k.cdc, totalRewards) + totalRewards := types.NewQueryDelegatorTotalRewardsResponse(delRewards, total) + bz, err := json.Marshal(totalRewards) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } diff --git a/x/distribution/keeper/querier_test.go b/x/distribution/keeper/querier_test.go index 8ba091bcf4..fb0b6c3d4a 100644 --- a/x/distribution/keeper/querier_test.go +++ b/x/distribution/keeper/querier_test.go @@ -109,6 +109,19 @@ func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec return } +func getQueriedDelegatorTotalRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, delegatorAddr sdk.AccAddress) (response types.QueryDelegatorTotalRewardsResponse) { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, QueryDelegatorTotalRewards}, "/"), + Data: cdc.MustMarshalJSON(NewQueryDelegatorParams(delegatorAddr)), + } + + bz, err := querier(ctx, []string{QueryDelegatorTotalRewards}, query) + require.Nil(t, err) + require.Nil(t, cdc.UnmarshalJSON(bz, &response)) + + return +} + func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (ptr []byte) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, QueryCommunityPool}, ""), @@ -124,6 +137,7 @@ func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, qu func TestQueries(t *testing.T) { cdc := codec.New() + types.RegisterCodec(cdc) ctx, _, keeper, sk, _ := CreateTestInputDefault(t, false, 100) querier := NewQuerier(keeper) @@ -154,6 +168,10 @@ func TestQueries(t *testing.T) { retCommission := getQueriedValidatorCommission(t, ctx, cdc, querier, valOpAddr1) require.Equal(t, commission, retCommission) + // test delegator's total rewards query + delRewards := getQueriedDelegatorTotalRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1)) + require.Equal(t, types.QueryDelegatorTotalRewardsResponse{}, delRewards) + // test validator slashes query with height range slashOne := types.NewValidatorSlashEvent(3, sdk.NewDecWithPrec(5, 1)) slashTwo := types.NewValidatorSlashEvent(7, sdk.NewDecWithPrec(6, 1)) @@ -183,6 +201,14 @@ func TestQueries(t *testing.T) { rewards = getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1) require.Equal(t, sdk.DecCoins{{sdk.DefaultBondDenom, sdk.NewDec(initial / 2)}}, rewards) + // test delegator's total rewards query + delRewards = getQueriedDelegatorTotalRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1)) + expectedDelReward := types.NewDelegationDelegatorReward(valOpAddr1, + sdk.DecCoins{sdk.NewInt64DecCoin("stake", 5)}) + wantDelRewards := types.NewQueryDelegatorTotalRewardsResponse( + []types.DelegationDelegatorReward{expectedDelReward}, expectedDelReward.Reward) + require.Equal(t, wantDelRewards, delRewards) + // currently community pool hold nothing so we should return null communityPool := getQueriedCommunityPool(t, ctx, cdc, querier) require.Nil(t, communityPool) diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 032cd3dfa3..c21d6a8d2c 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -123,7 +123,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64, // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range TestAddrs { pool := sk.GetPool(ctx) - _, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{ + _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{ sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins), }) require.Nil(t, err) diff --git a/x/distribution/tags/tags.go b/x/distribution/tags/tags.go index 21628daedb..8c84cddeae 100644 --- a/x/distribution/tags/tags.go +++ b/x/distribution/tags/tags.go @@ -1,4 +1,3 @@ -// nolint package tags import ( @@ -6,6 +5,9 @@ import ( ) var ( + TxCategory = "distribution" + Validator = sdk.TagSrcValidator - Delegator = sdk.TagDelegator + Category = sdk.TagCategory + Sender = sdk.TagSender ) diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index 4c09613290..9d2c6a4e53 100644 --- a/x/distribution/types/expected_keepers.go +++ b/x/distribution/types/expected_keepers.go @@ -20,7 +20,7 @@ type StakingKeeper interface { // expected coin keeper type BankKeeper interface { - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) } // expected fee collection keeper diff --git a/x/distribution/types/msg.go b/x/distribution/types/msg.go index 2f773d4732..4e39aaedb2 100644 --- a/x/distribution/types/msg.go +++ b/x/distribution/types/msg.go @@ -96,7 +96,7 @@ func NewMsgWithdrawValidatorCommission(valAddr sdk.ValAddress) MsgWithdrawValida } func (msg MsgWithdrawValidatorCommission) Route() string { return ModuleName } -func (msg MsgWithdrawValidatorCommission) Type() string { return "withdraw_validator_rewards_all" } +func (msg MsgWithdrawValidatorCommission) Type() string { return "withdraw_validator_commission" } // Return address that must sign over msg.GetSignBytes() func (msg MsgWithdrawValidatorCommission) GetSigners() []sdk.AccAddress { diff --git a/x/distribution/types/query.go b/x/distribution/types/query.go new file mode 100644 index 0000000000..7eeadaa480 --- /dev/null +++ b/x/distribution/types/query.go @@ -0,0 +1,46 @@ +package types + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryDelegatorTotalRewardsResponse defines the properties of +// QueryDelegatorTotalRewards query's response. +type QueryDelegatorTotalRewardsResponse struct { + Rewards []DelegationDelegatorReward `json:"rewards"` + Total sdk.DecCoins `json:"total"` +} + +// NewQueryDelegatorTotalRewardsResponse constructs a QueryDelegatorTotalRewardsResponse +func NewQueryDelegatorTotalRewardsResponse(rewards []DelegationDelegatorReward, + total sdk.DecCoins) QueryDelegatorTotalRewardsResponse { + return QueryDelegatorTotalRewardsResponse{Rewards: rewards, Total: total} +} + +func (res QueryDelegatorTotalRewardsResponse) String() string { + out := "Delegator Total Rewards:\n" + out += " Rewards:" + for _, reward := range res.Rewards { + out += fmt.Sprintf(` + ValidatorAddress: %s + Reward: %s`, reward.ValidatorAddress, reward.Reward) + } + out += fmt.Sprintf("\n Total: %s\n", res.Total) + return strings.TrimSpace(out) +} + +// DelegationDelegatorReward defines the properties +// of a delegator's delegation reward. +type DelegationDelegatorReward struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Reward sdk.DecCoins `json:"reward"` +} + +// NewDelegationDelegatorReward constructs a DelegationDelegatorReward. +func NewDelegationDelegatorReward(valAddr sdk.ValAddress, + reward sdk.DecCoins) DelegationDelegatorReward { + return DelegationDelegatorReward{ValidatorAddress: valAddr, Reward: reward} +} diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 836c799b0d..b8b4587c3a 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -79,26 +79,15 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome WithCodec(cdc). WithAccountDecoder(cdc) - // Get from address + // Get proposer address from := cliCtx.GetFromAddress() - // Pull associated account - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - // Find deposit amount amount, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } - // ensure account has enough coins - if !account.GetCoins().IsAllGTE(amount) { - return fmt.Errorf("address %s doesn't have enough coins to pay for this transaction", from) - } - proposalType, err := gov.ProposalTypeFromString(proposal.Type) if err != nil { return err @@ -152,25 +141,15 @@ $ gaiacli tx gov deposit 1 10stake --from mykey return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err) } + // Get depositor address from := cliCtx.GetFromAddress() - // Fetch associated account - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - // Get amount of coins amount, err := sdk.ParseCoins(args[1]) if err != nil { return err } - // ensure account has enough coins - if !account.GetCoins().IsAllGTE(amount) { - return fmt.Errorf("address %s doesn't have enough coins to pay for this transaction", from) - } - msg := gov.NewMsgDeposit(from, proposalID, amount) err = msg.ValidateBasic() if err != nil { diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index dadd7dd534..351c831435 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -129,7 +129,7 @@ func QueryVoteByTxQuery( tags := []string{ fmt.Sprintf("%s='%s'", tags.Action, gov.MsgVote{}.Type()), fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - fmt.Sprintf("%s='%s'", tags.Voter, []byte(params.Voter.String())), + fmt.Sprintf("%s='%s'", tags.Sender, []byte(params.Voter.String())), } // NOTE: SearchTxs is used to facilitate the txs query which does not currently @@ -172,7 +172,7 @@ func QueryDepositByTxQuery( tags := []string{ fmt.Sprintf("%s='%s'", tags.Action, gov.MsgDeposit{}.Type()), fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - fmt.Sprintf("%s='%s'", tags.Depositor, []byte(params.Depositor.String())), + fmt.Sprintf("%s='%s'", tags.Sender, []byte(params.Depositor.String())), } // NOTE: SearchTxs is used to facilitate the txs query which does not currently diff --git a/x/gov/endblocker.go b/x/gov/endblocker.go index 3da7f3a1da..41923e3396 100644 --- a/x/gov/endblocker.go +++ b/x/gov/endblocker.go @@ -9,7 +9,7 @@ import ( // Called every block, process inflation, update validator set func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { - logger := ctx.Logger().With("module", "x/gov") + logger := keeper.Logger(ctx) resTags := sdk.NewTags() inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) diff --git a/x/gov/expected_keepers.go b/x/gov/expected_keepers.go index 513c08ca07..bc80bcedcb 100644 --- a/x/gov/expected_keepers.go +++ b/x/gov/expected_keepers.go @@ -7,6 +7,6 @@ type BankKeeper interface { GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins // TODO remove once governance doesn't require use of accounts - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error SetSendEnabled(ctx sdk.Context, enabled bool) } diff --git a/x/gov/handler.go b/x/gov/handler.go index e51f118951..a4a6494c33 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -13,12 +13,15 @@ func NewHandler(keeper Keeper) sdk.Handler { switch msg := msg.(type) { case MsgDeposit: return handleMsgDeposit(ctx, keeper, msg) + case MsgSubmitProposal: return handleMsgSubmitProposal(ctx, keeper, msg) + case MsgVote: return handleMsgVote(ctx, keeper, msg) + default: - errMsg := fmt.Sprintf("Unrecognized gov msg type: %T", msg) + errMsg := fmt.Sprintf("unrecognized gov message type: %T", msg) return sdk.ErrUnknownRequest(errMsg).Result() } } @@ -47,8 +50,9 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos } resTags := sdk.NewTags( - tags.Proposer, []byte(msg.Proposer.String()), tags.ProposalID, proposalIDStr, + tags.Category, tags.TxCategory, + tags.Sender, msg.Proposer.String(), ) if votingStarted { @@ -68,9 +72,11 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result } proposalIDStr := fmt.Sprintf("%d", msg.ProposalID) + resTags := sdk.NewTags( - tags.Depositor, []byte(msg.Depositor.String()), tags.ProposalID, proposalIDStr, + tags.Category, tags.TxCategory, + tags.Sender, msg.Depositor.String(), ) if votingStarted { @@ -88,10 +94,13 @@ func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result { return err.Result() } + proposalIDStr := fmt.Sprintf("%d", msg.ProposalID) + return sdk.Result{ Tags: sdk.NewTags( - tags.Voter, msg.Voter.String(), - tags.ProposalID, fmt.Sprintf("%d", msg.ProposalID), + tags.ProposalID, proposalIDStr, + tags.Category, tags.TxCategory, + tags.Sender, msg.Voter.String(), ), } } diff --git a/x/gov/handler_test.go b/x/gov/handler_test.go new file mode 100644 index 0000000000..8b8ad17cb5 --- /dev/null +++ b/x/gov/handler_test.go @@ -0,0 +1,19 @@ +package gov + +import ( + "strings" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/stretchr/testify/require" +) + +func TestInvalidMsg(t *testing.T) { + k := Keeper{} + h := NewHandler(k) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized gov message type")) +} diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 46085cf2d9..f64b0fbc6c 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/params" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" ) const ( @@ -94,6 +95,9 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, } } +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/gov") } + // Proposals func (keeper Keeper) SubmitProposal(ctx sdk.Context, content ProposalContent) (proposal Proposal, err sdk.Error) { proposalID, err := keeper.getNewProposalID(ctx) @@ -382,7 +386,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd // Send coins from depositor's account to DepositedCoinsAccAddr account // TODO: Don't use an account for this purpose; it's clumsy and prone to misuse. - _, err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount) + err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount) if err != nil { return err, false } @@ -426,7 +430,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) - _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount) + err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount) if err != nil { panic("should not happen") } @@ -445,7 +449,7 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) // TODO: Find a way to do this without using accounts. - _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount) + err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount) if err != nil { panic("should not happen") } diff --git a/x/gov/tags/tags.go b/x/gov/tags/tags.go index 6f18b2596b..5960c6efdd 100644 --- a/x/gov/tags/tags.go +++ b/x/gov/tags/tags.go @@ -9,12 +9,12 @@ var ( ActionProposalDropped = "proposal-dropped" ActionProposalPassed = "proposal-passed" ActionProposalRejected = "proposal-rejected" + TxCategory = "governance" Action = sdk.TagAction - Proposer = "proposer" + Category = sdk.TagCategory + Sender = sdk.TagSender ProposalID = "proposal-id" VotingPeriodStart = "voting-period-start" - Depositor = "depositor" - Voter = "voter" ProposalResult = "proposal-result" ) diff --git a/x/ibc/expected_keepers.go b/x/ibc/expected_keepers.go index f2b65f1624..54b70b4dd8 100644 --- a/x/ibc/expected_keepers.go +++ b/x/ibc/expected_keepers.go @@ -4,6 +4,6 @@ import sdk "github.com/cosmos/cosmos-sdk/types" // expected bank keeper type BankKeeper interface { - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) } diff --git a/x/ibc/handler.go b/x/ibc/handler.go index 7d7bda5759..b1b958218a 100644 --- a/x/ibc/handler.go +++ b/x/ibc/handler.go @@ -1,6 +1,8 @@ package ibc import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,10 +11,12 @@ func NewHandler(ibcm Mapper, ck BankKeeper) sdk.Handler { switch msg := msg.(type) { case MsgIBCTransfer: return handleIBCTransferMsg(ctx, ibcm, ck, msg) + case MsgIBCReceive: return handleIBCReceiveMsg(ctx, ibcm, ck, msg) + default: - errMsg := "Unrecognized IBC Msg type: " + msg.Type() + errMsg := fmt.Sprintf("unrecognized IBC message type: %T", msg) return sdk.ErrUnknownRequest(errMsg).Result() } } @@ -22,7 +26,7 @@ func NewHandler(ibcm Mapper, ck BankKeeper) sdk.Handler { func handleIBCTransferMsg(ctx sdk.Context, ibcm Mapper, ck BankKeeper, msg MsgIBCTransfer) sdk.Result { packet := msg.IBCPacket - _, _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) + _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) if err != nil { return err.Result() } @@ -45,7 +49,7 @@ func handleIBCReceiveMsg(ctx sdk.Context, ibcm Mapper, ck BankKeeper, msg MsgIBC } // XXX Check that packet.Coins is valid and positive (nonzero) - _, _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) + _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) if err != nil { return err.Result() } diff --git a/x/ibc/handler_test.go b/x/ibc/handler_test.go new file mode 100644 index 0000000000..fca5634747 --- /dev/null +++ b/x/ibc/handler_test.go @@ -0,0 +1,19 @@ +package ibc + +import ( + "strings" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/stretchr/testify/require" +) + +func TestInvalidMsg(t *testing.T) { + m := Mapper{} + h := NewHandler(m, nil) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized IBC message type")) +} diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index b796b9c63d..286da66fd9 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -83,7 +83,7 @@ func newAddress() sdk.AccAddress { func getCoins(ck bank.Keeper, ctx sdk.Context, addr sdk.AccAddress) (sdk.Coins, sdk.Error) { zero := sdk.Coins(nil) - coins, _, err := ck.AddCoins(ctx, addr, zero) + coins, err := ck.AddCoins(ctx, addr, zero) return coins, err } @@ -97,7 +97,7 @@ func TestIBC(t *testing.T) { zero := sdk.Coins(nil) mycoins := sdk.Coins{sdk.NewInt64Coin("mycoin", 10)} - coins, _, err := input.bk.AddCoins(ctx, src, mycoins) + coins, err := input.bk.AddCoins(ctx, src, mycoins) require.Nil(t, err) require.Equal(t, mycoins, coins) diff --git a/x/mint/client/cli/query.go b/x/mint/client/cli/query.go index 4980240730..65bde6adeb 100644 --- a/x/mint/client/cli/query.go +++ b/x/mint/client/cli/query.go @@ -3,11 +3,12 @@ package cli import ( "fmt" + "github.com/spf13/cobra" + "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/mint" - "github.com/spf13/cobra" ) // GetCmdQueryParams implements a command to return the current minting diff --git a/x/mint/client/module_client.go b/x/mint/client/module_client.go index 857bb79926..98b2488be0 100644 --- a/x/mint/client/module_client.go +++ b/x/mint/client/module_client.go @@ -1,15 +1,14 @@ -package clientpackage +package client import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/mint" - "github.com/cosmos/cosmos-sdk/x/mint/client/cli" - "github.com/spf13/cobra" "github.com/tendermint/go-amino" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/mint/client/cli" ) -// ModuleClient exports all CLI client functionality from the minting module. type ModuleClient struct { storeKey string cdc *amino.Codec @@ -27,7 +26,7 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command { } mintingQueryCmd.AddCommand( - client.GetCommands( + sdkclient.GetCommands( cli.GetCmdQueryParams(mc.cdc), cli.GetCmdQueryInflation(mc.cdc), cli.GetCmdQueryAnnualProvisions(mc.cdc), diff --git a/x/mint/client/rest/query.go b/x/mint/client/rest/query.go index 9bc436a6d5..2d3cd4a0fe 100644 --- a/x/mint/client/rest/query.go +++ b/x/mint/client/rest/query.go @@ -4,11 +4,12 @@ import ( "fmt" "net/http" + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/mint" - "github.com/gorilla/mux" ) func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { diff --git a/x/mint/client/rest/rest.go b/x/mint/client/rest/rest.go index cedfa72c74..82a1afdd18 100644 --- a/x/mint/client/rest/rest.go +++ b/x/mint/client/rest/rest.go @@ -1,9 +1,10 @@ package rest import ( + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" - "github.com/gorilla/mux" ) // RegisterRoutes registers minting module REST handlers on the provided router. diff --git a/x/mint/querier.go b/x/mint/querier.go index 21d35ce15c..4de3fdeeb5 100644 --- a/x/mint/querier.go +++ b/x/mint/querier.go @@ -3,9 +3,10 @@ package mint import ( "fmt" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) // Query endpoints supported by the minting querier diff --git a/x/mint/querier_test.go b/x/mint/querier_test.go index e9f3c8d6c6..7a73853d0a 100644 --- a/x/mint/querier_test.go +++ b/x/mint/querier_test.go @@ -3,9 +3,10 @@ package mint import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/x/mint/test_common.go b/x/mint/test_common.go index f699acda23..f2bec95157 100644 --- a/x/mint/test_common.go +++ b/x/mint/test_common.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,7 +19,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking" - dbm "github.com/tendermint/tendermint/libs/db" ) type testInput struct { diff --git a/x/simulation/event_stats.go b/x/simulation/event_stats.go index f54eef4cc9..ad8acddb25 100644 --- a/x/simulation/event_stats.go +++ b/x/simulation/event_stats.go @@ -2,6 +2,7 @@ package simulation import ( "fmt" + "io" "sort" ) @@ -17,14 +18,16 @@ func (es eventStats) tally(eventDesc string) { } // Pretty-print events as a table -func (es eventStats) Print() { +func (es eventStats) Print(w io.Writer) { var keys []string for key := range es { keys = append(keys, key) } + sort.Strings(keys) - fmt.Printf("Event statistics: \n") + fmt.Fprintf(w, "Event statistics: \n") + for _, key := range keys { - fmt.Printf(" % 60s => %d\n", key, es[key]) + fmt.Fprintf(w, " % 60s => %d\n", key, es[key]) } } diff --git a/x/simulation/params.go b/x/simulation/params.go index 8499e6c118..6202ec6fef 100644 --- a/x/simulation/params.go +++ b/x/simulation/params.go @@ -60,7 +60,7 @@ func DefaultParams() Params { func RandomParams(r *rand.Rand) Params { return Params{ PastEvidenceFraction: r.Float64(), - NumKeys: r.Intn(250), + NumKeys: RandIntBetween(r, 2, 250), EvidenceFraction: r.Float64(), InitialLivenessWeightings: []int{r.Intn(80), r.Intn(10), r.Intn(10)}, LivenessTransitionMatrix: defaultLivenessTransitionMatrix, diff --git a/x/simulation/rand_util.go b/x/simulation/rand_util.go index a9b32ca48d..9677f2a612 100644 --- a/x/simulation/rand_util.go +++ b/x/simulation/rand_util.go @@ -72,6 +72,11 @@ func RandTimestamp(r *rand.Rand) time.Time { return time.Unix(unixTime, 0) } +// RandIntBetween returns a random int between two numbers inclusively. +func RandIntBetween(r *rand.Rand, min, max int) int { + return r.Intn(max-min) + min +} + // Derive a new rand deterministically from a rand. // Unlike rand.New(rand.NewSource(seed)), the result is "more random" // depending on the source and state of r. diff --git a/x/simulation/simulate.go b/x/simulation/simulate.go index 3eb25ccbc6..86e238a12d 100644 --- a/x/simulation/simulate.go +++ b/x/simulation/simulate.go @@ -3,6 +3,7 @@ package simulation import ( "encoding/json" "fmt" + "io" "math/rand" "os" "os/signal" @@ -26,7 +27,7 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, invariants sdk.Invariants, numBlocks, blockSize int, commit, lean bool) (bool, error) { time := time.Now().UnixNano() - return SimulateFromSeed(t, app, appStateFn, time, ops, + return SimulateFromSeed(t, os.Stdout, app, appStateFn, time, ops, invariants, numBlocks, blockSize, commit, lean) } @@ -51,19 +52,20 @@ func initChain( // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. // TODO split this monster function up -func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, +func SimulateFromSeed( + tb testing.TB, w io.Writer, app *baseapp.BaseApp, appStateFn AppStateFn, seed int64, ops WeightedOperations, invariants sdk.Invariants, - numBlocks, blockSize int, commit, lean bool) (stopEarly bool, simError error) { + numBlocks, blockSize int, commit, lean bool, +) (stopEarly bool, simError error) { // in case we have to end early, don't os.Exit so that we can run cleanup code. testingMode, t, b := getTestingMode(tb) - fmt.Printf("Starting SimulateFromSeed with randomness "+ - "created with seed %d\n", int(seed)) + fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) r := rand.New(rand.NewSource(seed)) params := RandomParams(r) // := DefaultParams() - fmt.Printf("Randomized simulation params: %+v\n", params) + fmt.Fprintf(w, "Randomized simulation params: %+v\n", params) genesisTimestamp := RandTimestamp(r) fmt.Printf("Starting the simulation from time %v, unixtime %v\n", @@ -94,8 +96,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) go func() { receivedSignal := <-c - fmt.Printf("\nExiting early due to %s, on block %d, operation %d\n", - receivedSignal, header.Height, opCount) + fmt.Fprintf(w, "\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) simError = fmt.Errorf("Exited due to %s", receivedSignal) stopEarly = true }() @@ -113,7 +114,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, logWriter := NewLogWriter(testingMode) blockSimulator := createBlockSimulator( - testingMode, tb, t, params, eventStats.tally, invariants, + testingMode, tb, t, w, params, eventStats.tally, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, logWriter, lean) @@ -123,13 +124,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Recover logs in case of panic defer func() { if r := recover(); r != nil { - fmt.Println("Panic with err\n", r) + fmt.Fprintf(w, "panic with err: %v\n", r) stackTrace := string(debug.Stack()) fmt.Println(stackTrace) logWriter.PrintLogs() - simError = fmt.Errorf( - "Simulation halted due to panic on block %d", - header.Height) + simError = fmt.Errorf("Simulation halted due to panic on block %d", header.Height) } }() } @@ -188,8 +187,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } if header.ProposerAddress == nil { - fmt.Printf("\nSimulation stopped early as all validators " + - "have been unbonded, there is nobody left propose a block!\n") + fmt.Fprintf(w, "\nSimulation stopped early as all validators have been unbonded; nobody left to propose a block!\n") stopEarly = true break } @@ -207,14 +205,17 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } if stopEarly { - eventStats.Print() + eventStats.Print(w) return true, simError } - fmt.Printf("\nSimulation complete. Final height (blocks): %d, "+ - "final time (seconds), : %v, operations ran %d\n", - header.Height, header.Time, opCount) - eventStats.Print() + fmt.Fprintf( + w, + "\nSimulation complete; Final height (blocks): %d, final time (seconds): %v, operations ran: %d\n", + header.Height, header.Time, opCount, + ) + + eventStats.Print(w) return false, nil } @@ -225,7 +226,7 @@ type blockSimFn func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, // Returns a function to simulate blocks. Written like this to avoid constant // parameters being passed everytime, to minimize memory overhead. -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params, +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Writer, params Params, event func(string), invariants sdk.Invariants, ops WeightedOperations, operationQueue OperationQueue, timeOperationQueue []FutureOperation, totalNumBlocks, avgBlockSize int, logWriter LogWriter, lean bool) blockSimFn { @@ -237,7 +238,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header) (opCount int) { - fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", + fmt.Fprintf(w, "\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) lastBlocksizeState, blocksize = getBlockSize(r, params, lastBlocksizeState, avgBlockSize) @@ -277,7 +278,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params assertAllInvariants(t, app, invariants, eventStr, logWriter) } if opCount%50 == 0 { - fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", + fmt.Fprintf(w, "\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) } } diff --git a/x/simulation/util.go b/x/simulation/util.go index 14df0f6c64..3badf25a9a 100644 --- a/x/simulation/util.go +++ b/x/simulation/util.go @@ -5,9 +5,10 @@ import ( "math/rand" "testing" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) // assertAll asserts the all invariants against application state @@ -74,3 +75,19 @@ func PeriodicInvariant(invariant sdk.Invariant, period int, offset int) sdk.Inva return nil } } + +// PeriodicInvariants returns an array of wrapped Invariants. Where each +// invariant function is only executed periodically defined by period and offset. +func PeriodicInvariants(invariants []sdk.Invariant, period int, offset int) []sdk.Invariant { + var outInvariants []sdk.Invariant + for _, invariant := range invariants { + outInvariant := func(ctx sdk.Context) error { + if int(ctx.BlockHeight())%period == offset { + return invariant(ctx) + } + return nil + } + outInvariants = append(outInvariants, outInvariant) + } + return outInvariants +} diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index 3c4afdd00e..5da8ad825d 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -2,14 +2,16 @@ package rest import ( "fmt" + "net/http" + + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/rpc" "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/slashing" - "github.com/gorilla/mux" - "net/http" ) func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { @@ -46,11 +48,6 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code return } - if code == http.StatusNoContent { - w.WriteHeader(http.StatusNoContent) - return - } - rest.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent) } } @@ -79,7 +76,7 @@ func signingInfoHandlerListFn(cliCtx context.CLIContext, storeName string, cdc * } if len(validators.Validators) == 0 { - w.WriteHeader(http.StatusNoContent) + rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent) return } @@ -98,7 +95,7 @@ func signingInfoHandlerListFn(cliCtx context.CLIContext, storeName string, cdc * } if len(signingInfoList) == 0 { - w.WriteHeader(http.StatusNoContent) + rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent) return } @@ -130,7 +127,7 @@ func getSigningInfo(cliCtx context.CLIContext, storeName string, cdc *codec.Code } if len(res) == 0 { - code = http.StatusNoContent + code = http.StatusOK return } diff --git a/x/slashing/client/rest/query_test.go b/x/slashing/client/rest/query_test.go index ad177c23b5..62afaef0de 100644 --- a/x/slashing/client/rest/query_test.go +++ b/x/slashing/client/rest/query_test.go @@ -1,8 +1,9 @@ package rest import ( - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" ) func TestAdjustPagination(t *testing.T) { diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 70a28089d0..d3562fe05d 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -1,6 +1,8 @@ package slashing import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/tags" ) @@ -11,8 +13,10 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgUnjail: return handleMsgUnjail(ctx, msg, k) + default: - return sdk.ErrTxDecode("invalid message parse in staking module").Result() + errMsg := fmt.Sprintf("unrecognized slashing message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() } } } @@ -61,8 +65,8 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { k.validatorSet.Unjail(ctx, consAddr) tags := sdk.NewTags( - tags.Action, tags.ActionValidatorUnjailed, - tags.Validator, msg.ValidatorAddr.String(), + tags.Category, tags.TxCategory, + tags.Sender, msg.ValidatorAddr.String(), ) return sdk.Result{ diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 18e03fba26..93a7057fb5 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -1,6 +1,7 @@ package slashing import ( + "strings" "testing" "time" @@ -121,3 +122,12 @@ func TestJailedValidatorDelegations(t *testing.T) { got = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr)) require.True(t, got.IsOK(), "expected jailed validator to be able to unjail, got: %v", got) } + +func TestInvalidMsg(t *testing.T) { + k := Keeper{} + h := NewHandler(k) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized slashing message type")) +} diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 35a357748f..dbd26db951 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -5,6 +5,7 @@ import ( "time" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,10 +35,13 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, paramspa return keeper } +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/slashing") } + // handle a validator signing two blocks at the same height // power: power of the double-signing validator at the height of infraction func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractionHeight int64, timestamp time.Time, power int64) { - logger := ctx.Logger().With("module", "x/slashing") + logger := k.Logger(ctx) // calculate the age of the evidence time := ctx.BlockHeader().Time @@ -125,7 +129,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // handle a validator signature, must be called once per validator per block // TODO refactor to take in a consensus address, additionally should maybe just take in the pubkey too func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { - logger := ctx.Logger().With("module", "x/slashing") + logger := k.Logger(ctx) height := ctx.BlockHeight() consAddr := sdk.ConsAddress(addr) pubkey, err := k.getPubkey(ctx, addr) diff --git a/x/slashing/tags/tags.go b/x/slashing/tags/tags.go index 12c1d616f4..48e5bcba2f 100644 --- a/x/slashing/tags/tags.go +++ b/x/slashing/tags/tags.go @@ -6,8 +6,8 @@ import ( // Slashing tags var ( - ActionValidatorUnjailed = "validator-unjailed" + TxCategory = "slashing" - Action = sdk.TagAction - Validator = "validator" + Category = sdk.TagCategory + Sender = sdk.TagSender ) diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 13279d614e..19286d23ef 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -81,7 +81,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s require.Nil(t, err) for _, addr := range addrs { - _, _, err = ck.AddCoins(ctx, sdk.AccAddress(addr), sdk.Coins{ + _, err = ck.AddCoins(ctx, sdk.AccAddress(addr), sdk.Coins{ {sk.GetParams(ctx).BondDenom, initCoins}, }) } diff --git a/x/slashing/tick.go b/x/slashing/tick.go index bd779973f1..14e07d6509 100644 --- a/x/slashing/tick.go +++ b/x/slashing/tick.go @@ -27,7 +27,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) sdk.Ta case tmtypes.ABCIEvidenceTypeDuplicateVote: sk.handleDoubleSign(ctx, evidence.Validator.Address, evidence.Height, evidence.Time, evidence.Validator.Power) default: - ctx.Logger().With("module", "x/slashing").Error(fmt.Sprintf("ignored unknown evidence type: %s", evidence.Type)) + sk.Logger(ctx).Error(fmt.Sprintf("ignored unknown evidence type: %s", evidence.Type)) } } diff --git a/x/staking/alias.go b/x/staking/alias.go index d1e48ef790..3c8e19fd9f 100644 --- a/x/staking/alias.go +++ b/x/staking/alias.go @@ -4,7 +4,6 @@ package staking import ( "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/querier" - "github.com/cosmos/cosmos-sdk/x/staking/tags" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -174,15 +173,3 @@ var ( ErrMinSelfDelegationDecreased = types.ErrMinSelfDelegationDecreased ErrSelfDelegationBelowMinimum = types.ErrSelfDelegationBelowMinimum ) - -var ( - ActionCompleteUnbonding = tags.ActionCompleteUnbonding - ActionCompleteRedelegation = tags.ActionCompleteRedelegation - - TagAction = tags.Action - TagSrcValidator = tags.SrcValidator - TagDstValidator = tags.DstValidator - TagDelegator = tags.Delegator - TagMoniker = tags.Moniker - TagIdentity = tags.Identity -) diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index a9439bd803..e7ba6e8347 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -10,7 +10,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/tags" "github.com/gorilla/mux" ) @@ -143,16 +142,12 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han actions = append(actions, staking.MsgDelegate{}.Type()) case isUnbondTx: actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, tags.ActionCompleteUnbonding) case isRedTx: actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, tags.ActionCompleteRedelegation) case noQuery: actions = append(actions, staking.MsgDelegate{}.Type()) actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, tags.ActionCompleteUnbonding) actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, tags.ActionCompleteRedelegation) default: w.WriteHeader(http.StatusNoContent) return diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index a34864e604..f6d4be4932 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -31,7 +31,7 @@ func queryTxs(cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegator limit := 100 tags := []string{ fmt.Sprintf("%s='%s'", tags.Action, tag), - fmt.Sprintf("%s='%s'", tags.Delegator, delegatorAddr), + fmt.Sprintf("%s='%s'", tags.Sender, delegatorAddr), } return tx.SearchTxs(cliCtx, cdc, tags, page, limit) diff --git a/x/staking/handler.go b/x/staking/handler.go index 7da83cec2e..bfff581f05 100644 --- a/x/staking/handler.go +++ b/x/staking/handler.go @@ -1,6 +1,7 @@ package staking import ( + "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -19,16 +20,22 @@ func NewHandler(k keeper.Keeper) sdk.Handler { switch msg := msg.(type) { case types.MsgCreateValidator: return handleMsgCreateValidator(ctx, msg, k) + case types.MsgEditValidator: return handleMsgEditValidator(ctx, msg, k) + case types.MsgDelegate: return handleMsgDelegate(ctx, msg, k) + case types.MsgBeginRedelegate: return handleMsgBeginRedelegate(ctx, msg, k) + case types.MsgUndelegate: return handleMsgUndelegate(ctx, msg, k) + default: - return sdk.ErrTxDecode("invalid message parse in staking module").Result() + errMsg := fmt.Sprintf("unrecognized staking message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() } } } @@ -60,7 +67,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T } resTags.AppendTags(sdk.NewTags( - tags.Action, ActionCompleteUnbonding, + tags.Action, tags.ActionCompleteUnbonding, tags.Delegator, dvPair.DelegatorAddress.String(), tags.SrcValidator, dvPair.ValidatorAddress.String(), )) @@ -77,6 +84,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T resTags.AppendTags(sdk.NewTags( tags.Action, tags.ActionCompleteRedelegation, + tags.Category, tags.TxCategory, tags.Delegator, dvvTriplet.DelegatorAddress.String(), tags.SrcValidator, dvvTriplet.ValidatorSrcAddress.String(), tags.DstValidator, dvvTriplet.ValidatorDstAddress.String(), @@ -142,14 +150,14 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k return err.Result() } - tags := sdk.NewTags( + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), tags.DstValidator, msg.ValidatorAddress.String(), - tags.Moniker, msg.Description.Moniker, - tags.Identity, msg.Description.Identity, ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -192,14 +200,13 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe k.SetValidator(ctx, validator) - tags := sdk.NewTags( - tags.DstValidator, msg.ValidatorAddress.String(), - tags.Moniker, description.Moniker, - tags.Identity, description.Identity, + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.ValidatorAddress.String(), ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -218,13 +225,14 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) return err.Result() } - tags := sdk.NewTags( - tags.Delegator, msg.DelegatorAddress.String(), + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), tags.DstValidator, msg.ValidatorAddress.String(), ) return sdk.Result{ - Tags: tags, + Tags: resTags, } } @@ -242,13 +250,14 @@ func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keep } finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) - tags := sdk.NewTags( - tags.Delegator, msg.DelegatorAddress.String(), + resTags := sdk.NewTags( + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), tags.SrcValidator, msg.ValidatorAddress.String(), tags.EndTime, completionTime.Format(time.RFC3339), ) - return sdk.Result{Data: finishTime, Tags: tags} + return sdk.Result{Data: finishTime, Tags: resTags} } func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result { @@ -268,7 +277,8 @@ func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k k finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) resTags := sdk.NewTags( - tags.Delegator, msg.DelegatorAddress.String(), + tags.Category, tags.TxCategory, + tags.Sender, msg.DelegatorAddress.String(), tags.SrcValidator, msg.ValidatorSrcAddress.String(), tags.DstValidator, msg.ValidatorDstAddress.String(), tags.EndTime, completionTime.Format(time.RFC3339), diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go index 19865aeba1..bdf0ab4699 100644 --- a/x/staking/handler_test.go +++ b/x/staking/handler_test.go @@ -1,6 +1,7 @@ package staking import ( + "strings" "testing" "time" @@ -1272,3 +1273,12 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { validator, _ = keeper.GetValidator(ctx, valA) require.Equal(t, validator.GetStatus(), sdk.Unbonding) } + +func TestInvalidMsg(t *testing.T) { + k := keep.Keeper{} + h := NewHandler(k) + + res := h(sdk.Context{}, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized staking message type")) +} diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index bfd60fa7ba..e02f535639 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -3,9 +3,10 @@ package keeper import ( "container/list" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -44,6 +45,9 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, bk types.BankKeeper, return keeper } +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/staking") } + // Set the validator hooks func (k *Keeper) SetHooks(sh sdk.StakingHooks) *Keeper { if k.hooks != nil { diff --git a/x/staking/keeper/slash.go b/x/staking/keeper/slash.go index dbd19a9410..fd0df8d596 100644 --- a/x/staking/keeper/slash.go +++ b/x/staking/keeper/slash.go @@ -22,7 +22,7 @@ import ( // Infraction was committed at the current height or at a past height, // not at a height in the future func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec) { - logger := ctx.Logger().With("module", "x/staking") + logger := k.Logger(ctx) if slashFactor.LT(sdk.ZeroDec()) { panic(fmt.Errorf("attempted to slash with a negative slash factor: %v", slashFactor)) @@ -137,7 +137,7 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { validator := k.mustGetValidatorByConsAddr(ctx, consAddr) k.jailValidator(ctx, validator) - logger := ctx.Logger().With("module", "x/staking") + logger := k.Logger(ctx) logger.Info(fmt.Sprintf("validator %s jailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return @@ -147,7 +147,7 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { validator := k.mustGetValidatorByConsAddr(ctx, consAddr) k.unjailValidator(ctx, validator) - logger := ctx.Logger().With("module", "x/staking") + logger := k.Logger(ctx) logger.Info(fmt.Sprintf("validator %s unjailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go index 8edbe8e367..9b5c442f12 100644 --- a/x/staking/keeper/test_common.go +++ b/x/staking/keeper/test_common.go @@ -131,7 +131,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context pool := keeper.GetPool(ctx) err := error(nil) if !initCoins.IsZero() { - _, _, err = ck.AddCoins(ctx, addr, sdk.Coins{ + _, err = ck.AddCoins(ctx, addr, sdk.Coins{ {keeper.BondDenom(ctx), initCoins}, }) } diff --git a/x/staking/tags/tags.go b/x/staking/tags/tags.go index a99135171e..8468748dbb 100644 --- a/x/staking/tags/tags.go +++ b/x/staking/tags/tags.go @@ -1,19 +1,20 @@ -// nolint package tags import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// staking tags var ( ActionCompleteUnbonding = "complete-unbonding" ActionCompleteRedelegation = "complete-redelegation" + TxCategory = "staking" Action = sdk.TagAction + Category = sdk.TagCategory + Sender = sdk.TagSender SrcValidator = sdk.TagSrcValidator DstValidator = sdk.TagDstValidator Delegator = sdk.TagDelegator - Moniker = "moniker" - Identity = "identity" EndTime = "end-time" )