Merge branch 'develop' into fix-iterator-leak

This commit is contained in:
Christopher Goes 2018-08-22 16:06:35 +02:00 committed by GitHub
commit 7b8c1c0e80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
102 changed files with 3700 additions and 16930 deletions

View File

@ -39,14 +39,6 @@ jobs:
paths:
- bin
- profiles
- save_cache:
key: v1-pkg-cache
paths:
- /go/pkg
- save_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /go/src/github.com/cosmos/cosmos-sdk
lint:
<<: *defaults
@ -54,10 +46,12 @@ jobs:
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: Get metalinter
command: |
@ -75,32 +69,71 @@ jobs:
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: Test cli
command: |
export PATH="$GOBIN:$PATH"
make test_cli
test_sim:
test_sim_modules:
<<: *defaults
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: Test simulation
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
export GAIA_SIMULATION_SEED=1531897442166404087
make test_sim
make get_vendor_deps
- run:
name: Test individual module simulations
command: |
export PATH="$GOBIN:$PATH"
make test_sim_modules
test_sim_gaia_nondeterminism:
<<: *defaults
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: Test individual module simulations
command: |
export PATH="$GOBIN:$PATH"
make test_sim_gaia_nondeterminism
test_sim_gaia_fast:
<<: *defaults
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: Test full Gaia simulation
command: |
export PATH="$GOBIN:$PATH"
make test_sim_gaia_fast
test_cover:
<<: *defaults
@ -108,17 +141,19 @@ jobs:
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p /tmp/logs
- run:
name: Run tests
command: |
export PATH="$GOBIN:$PATH"
make install
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | grep -v '/simulation' | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
GOCACHE=off go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
done
@ -135,8 +170,12 @@ jobs:
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: gather
command: |
@ -150,6 +189,29 @@ jobs:
name: upload
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
localnet:
working_directory: /home/circleci/.go_workspace/src/github.com/cosmos/cosmos-sdk
machine:
image: circleci/classic:latest
environment:
GOBIN: /home/circleci/.go_workspace/bin
GOPATH: /home/circleci/.go_workspace/
GOOS: linux
GOARCH: amd64
parallelism: 1
steps:
- checkout
- run:
name: run localnet and exit on failure
command: |
set -x
make get_tools
make get_vendor_deps
make build-linux
make localnet-start
./scripts/localnet-blocks-test.sh 40 5 10 localhost
workflows:
version: 2
test-suite:
@ -161,12 +223,21 @@ workflows:
- test_cli:
requires:
- setup_dependencies
- test_sim:
- test_sim_modules:
requires:
- setup_dependencies
- test_sim_gaia_nondeterminism:
requires:
- setup_dependencies
- test_sim_gaia_fast:
requires:
- setup_dependencies
- test_cover:
requires:
- setup_dependencies
- localnet:
requires:
- setup_dependencies
- upload_coverage:
requires:
- test_cover

View File

@ -5,13 +5,14 @@ v If a checkbox is n/a - please still include it but + a little note why
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
- [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work.
- [ ] Updated all relevant documentation (`docs/`)
- [ ] Updated all relevant code comments
- [ ] Wrote tests
- [ ] Added entries in `PENDING.md` that include links to the relevant issue or PR that most accurately describes the change.
- [ ] Updated `cmd/gaia` and `examples/`
___________________________________
- [ ] Updated relevant documentation (`docs/`)
- [ ] Added entries in `PENDING.md` with issue #
- [ ] rereviewed `Files changed` in the github PR explorer
______
For Admin Use:
- [ ] Added appropriate labels to PR (ex. wip, ready-for-review, docs)
- [ ] Reviewers Assigned
- [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr))
- Added appropriate labels to PR (ex. wip, ready-for-review, docs)
- Reviewers Assigned
- Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr))

View File

@ -1,5 +1,128 @@
# Changelog
## 0.24.1
*August 21st, 2018*
BUG FIXES
* Gaia
- [x/slashing] Evidence tracking now uses validator address instead of validator pubkey
## 0.24.0
*August 13th, 2018*
BREAKING CHANGES
* Gaia REST API (`gaiacli advanced rest-server`)
- [x/stake] \#1880 More REST-ful endpoints (large refactor)
- [x/slashing] \#1866 `/slashing/signing_info` takes cosmosvalpub instead of cosmosvaladdr
- use time.Time instead of int64 for time. See Tendermint v0.23.0
- Signatures are no longer Amino encoded with prefixes (just encoded as raw
bytes) - see Tendermint v0.23.0
* Gaia CLI (`gaiacli`)
- [x/stake] change `--keybase-sig` to `--identity`
- [x/stake] \#1828 Force user to specify amount on create-validator command by removing default
- [x/gov] Change `--proposalID` to `--proposal-id`
- [x/stake, x/gov] \#1606 Use `--from` instead of adhoc flags like `--address-validator`
and `--proposer` to indicate the sender address.
- \#1551 Remove `--name` completely
- Genesis/key creation (`gaiad init`) now supports user-provided key passwords
* Gaia
- [x/stake] Inflation doesn't use rationals in calculation (performance boost)
- [x/stake] Persist a map from `addr->pubkey` in the state since BeginBlock
doesn't provide pubkeys.
- [x/gov] \#1781 Added tags sub-package, changed tags to use dash-case
- [x/gov] \#1688 Governance parameters are now stored in globalparams store
- [x/gov] \#1859 Slash validators who do not vote on a proposal
- [x/gov] \#1914 added TallyResult type that gets stored in Proposal after tallying is finished
* SDK
- [baseapp] Msgs are no longer run on CheckTx, removed `ctx.IsCheckTx()`
- [baseapp] NewBaseApp constructor takes sdk.TxDecoder as argument instead of wire.Codec
- [types] sdk.NewCoin takes sdk.Int, sdk.NewInt64Coin takes int64
- [x/auth] Default TxDecoder can be found in `x/auth` rather than baseapp
- [client] \#1551: Refactored `CoreContext` to `TxContext` and `QueryContext`
- Removed all tx related fields and logic (building & signing) to separate
structure `TxContext` in `x/auth/client/context`
* Tendermint
- v0.22.5 -> See [Tendermint PR](https://github.com/tendermint/tendermint/pull/1966)
- change all the cryptography imports.
- v0.23.0 -> See
[Changelog](https://github.com/tendermint/tendermint/blob/v0.23.0/CHANGELOG.md#0230)
and [SDK PR](https://github.com/cosmos/cosmos-sdk/pull/1927)
- BeginBlock no longer includes crypto.Pubkey
- use time.Time instead of int64 for time.
FEATURES
* Gaia REST API (`gaiacli advanced rest-server`)
- [x/gov] Can now query governance proposals by ProposalStatus
* Gaia CLI (`gaiacli`)
- [x/gov] added `query-proposals` command. Can filter by `depositer`, `voter`, and `status`
- [x/stake] \#2043 Added staking query cli cmds for unbonding-delegations and redelegations
* Gaia
- [networks] Added ansible scripts to upgrade seed nodes on a network
* SDK
- [x/mock/simulation] Randomized simulation framework
- Modules specify invariants and operations, preferably in an x/[module]/simulation package
- Modules can test random combinations of their own operations
- Applications can integrate operations and invariants from modules together for an integrated simulation
- Simulates Tendermint's algorithm for validator set updates
- Simulates validator signing/downtime with a Markov chain, and occaisional double-signatures
- Includes simulated operations & invariants for staking, slashing, governance, and bank modules
- [store] \#1481 Add transient store
- [baseapp] Initialize validator set on ResponseInitChain
- [baseapp] added BaseApp.Seal - ability to seal baseapp parameters once they've been set
- [cosmos-sdk-cli] New `cosmos-sdk-cli` tool to quickly initialize a new
SDK-based project
- [scripts] added log output monitoring to DataDog using Ansible scripts
IMPROVEMENTS
* Gaia
- [spec] \#967 Inflation and distribution specs drastically improved
- [x/gov] \#1773 Votes on a proposal can now be queried
- [x/gov] Initial governance parameters can now be set in the genesis file
- [x/stake] \#1815 Sped up the processing of `EditValidator` txs.
- [config] \#1930 Transactions indexer indexes all tags by default.
- [ci] [#2057](https://github.com/cosmos/cosmos-sdk/pull/2057) Run `make localnet-start` on every commit and ensure network reaches at least 10 blocks
* SDK
- [baseapp] \#1587 Allow any alphanumeric character in route
- [baseapp] Allow any alphanumeric character in route
- [tools] Remove `rm -rf vendor/` from `make get_vendor_deps`
- [x/auth] Recover ErrorOutOfGas panic in order to set sdk.Result attributes correctly
- [x/bank] Unit tests are now table-driven
- [tests] Add tests to example apps in docs
- [tests] Fixes ansible scripts to work with AWS too
- [tests] \#1806 CLI tests are now behind the build flag 'cli_test', so go test works on a new repo
BUG FIXES
* Gaia CLI (`gaiacli`)
- \#1766 Fixes bad example for keybase identity
- [x/stake] \#2021 Fixed repeated CLI commands in staking
* Gaia
- [x/stake] [#2077](https://github.com/cosmos/cosmos-sdk/pull/2077) Fixed invalid cliff power comparison
- \#1804 Fixes gen-tx genesis generation logic temporarily until upstream updates
- \#1799 Fix `gaiad export`
- \#1839 Fixed bug where intra-tx counter wasn't set correctly for genesis validators
- [x/stake] \#1858 Fixed bug where the cliff validator was not updated correctly
- [tests] \#1675 Fix non-deterministic `test_cover`
- [tests] \#1551 Fixed invalid LCD test JSON payload in `doIBCTransfer`
- [basecoin] Fixes coin transaction failure and account query [discussion](https://forum.cosmos.network/t/unmarshalbinarybare-expected-to-read-prefix-bytes-75fbfab8-since-it-is-registered-concrete-but-got-0a141dfa/664/6)
- [x/gov] \#1757 Fix VoteOption conversion to String
* [x/stake] [#2083] Fix broken invariant of bonded validator power decrease
## 0.23.1
*July 27th, 2018*
@ -167,7 +290,7 @@ FEATURES
* [types] Added MinInt and MinUint functions
* [gaiad] `unsafe_reset_all` now resets addrbook.json
* [democoin] add x/oracle, x/assoc
* [tests] created a randomized testing framework.
* [tests] created a randomized testing framework.
- Currently bank has limited functionality in the framework
- Auth has its invariants checked within the framework
* [tests] Add WaitForNextNBlocksTM helper method

35
Gopkg.lock generated
View File

@ -48,12 +48,12 @@
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
@ -376,7 +376,7 @@
[[projects]]
branch = "master"
digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b"
digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
@ -393,7 +393,7 @@
"leveldb/util",
]
pruneopts = "UT"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
[[projects]]
branch = "master"
@ -408,12 +408,12 @@
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:e9113641c839c21d8eaeb2c907c7276af1eddeed988df8322168c56b7e06e0e1"
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
[[projects]]
digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2"
@ -490,6 +490,14 @@
revision = "013b9cef642f875634c614019ab13b17570778ad"
version = "v0.23.0"
[[projects]]
digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e"
name = "github.com/tendermint/tmlibs"
packages = ["cli"]
pruneopts = "UT"
revision = "49596e0a1f48866603813df843c9409fc19805c6"
version = "v0.9.0"
[[projects]]
digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6"
name = "github.com/zondax/ledger-goclient"
@ -499,7 +507,7 @@
[[projects]]
branch = "master"
digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5"
digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a"
name = "golang.org/x/crypto"
packages = [
"blowfish",
@ -518,7 +526,7 @@
"salsa20/salsa",
]
pruneopts = "UT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
@ -538,14 +546,14 @@
[[projects]]
branch = "master"
digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54"
digest = "1:a0e12bc26f317c0e2d497baf767285e1790e526e8dd46553c5a67fcbc8692157"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf"
revision = "3b58ed4ad3395d483fc92d5d14123ce2c3581fec"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -576,7 +584,7 @@
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "383e8b2c3b9e36c4076b235b32537292176bae20"
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
@ -666,6 +674,7 @@
"github.com/tendermint/tendermint/rpc/lib/server",
"github.com/tendermint/tendermint/types",
"github.com/tendermint/tendermint/version",
"github.com/tendermint/tmlibs/cli",
"github.com/zondax/ledger-goclient",
"golang.org/x/crypto/blowfish",
"golang.org/x/crypto/ripemd160",

View File

@ -49,7 +49,7 @@
[[override]]
name = "github.com/tendermint/go-amino"
version = "=0.10.1"
version = "=v0.12.0-rc0"
[[override]]
name = "github.com/tendermint/iavl"

View File

@ -16,20 +16,20 @@ ci: get_tools get_vendor_deps install test_cover test_lint test
########################################
### Build/Install
check-ledger:
check-ledger:
ifeq ($(LEDGER_ENABLED),true)
ifeq ($(UNAME_S),OpenBSD)
$(info "OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)")
TMP_BUILD_TAGS := $(BUILD_TAGS)
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
TMP_BUILD_TAGS := $(BUILD_TAGS)
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
else
ifndef GCC
$(error "gcc not installed for ledger support, please install or set LEDGER_ENABLED to false in the Makefile")
endif
endif
else
TMP_BUILD_TAGS := $(BUILD_TAGS)
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
TMP_BUILD_TAGS := $(BUILD_TAGS)
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
endif
build: check-ledger
@ -138,15 +138,21 @@ test_unit:
test_race:
@go test -race $(PACKAGES_NOSIMULATION)
test_sim:
@echo "Running individual module simulations."
@go test $(PACKAGES_SIMTEST) -v
@echo "Running full Gaia simulation. This may take several minutes."
@echo "Pass the flag 'SimulationSeed' to run with a constant seed."
@echo "Pass the flag 'SimulationNumKeys' to run with the specified number of keys."
@echo "Pass the flag 'SimulationNumBlocks' to run with the specified number of blocks."
@echo "Pass the flag 'SimulationBlockSize' to run with the specified block size (operations per block)."
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationBlockSize=200 -v
test_sim_modules:
@echo "Running individual module simulations..."
@go test $(PACKAGES_SIMTEST)
test_sim_gaia_nondeterminism:
@echo "Running nondeterminism test..."
@go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m
test_sim_gaia_fast:
@echo "Running quick Gaia simulation. This may take several minutes..."
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=200 -timeout 24h
test_sim_gaia_slow:
@echo "Running full Gaia simulation. This may take awhile!"
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -v -timeout 24h
test_cover:
@bash tests/test_cover.sh
@ -198,8 +204,8 @@ build-docker-gaiadnode:
# Run a 4-node testnet locally
localnet-start: localnet-stop
@if ! [ -f build/node0/gaiad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/gaiad:Z tendermint/gaiadnode testnet --v 4 --o . --starting-ip-address 192.168.10.2 ; fi
docker-compose up
@if ! [ -f build/node0/gaiad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/gaiad:Z tendermint/gaiadnode testnet --v 4 -o . --starting-ip-address 192.168.10.2 ; fi
docker-compose up -d
# Stop testnet
localnet-stop:
@ -212,4 +218,4 @@ localnet-stop:
check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
build-linux build-docker-gaiadnode localnet-start localnet-stop \
format check-ledger test_sim update_tools update_dev_tools
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_slow update_tools update_dev_tools

View File

@ -1,108 +1,76 @@
## v0.24.0 PENDING
^--- PENDING wasn't purged on sdk v0.23.0 release.
BREAKING CHANGES
* Update to tendermint v0.23.0. This involves removing crypto.Pubkey,
maintaining a validator address to pubkey map, and using time.Time instead of int64 for time. [SDK PR](https://github.com/cosmos/cosmos-sdk/pull/1927)
## PENDING
BREAKING CHANGES
* API
- \#1880 and \#2000 [x/stake] changed the endpoints to be more REST-ful
* Update to tendermint v0.22.5. This involves changing all of the cryptography imports. [Ref](https://github.com/tendermint/tendermint/pull/1966)
* [baseapp] Msgs are no longer run on CheckTx, removed `ctx.IsCheckTx()`
* [x/gov] CLI flag changed from `proposalID` to `proposal-id`
* [x/stake] Fixed the period check for the inflation calculation
* [x/stake] Inflation doesn't use rationals in calculation (performance boost)
* [x/stake] CLI flags for identity changed from `--keybase-sig` to `--identity`, effects:
* `gaiacli stake create-validator`
* `gaiacli stake edit-validator`
* [baseapp] NewBaseApp constructor now takes sdk.TxDecoder as argument instead of wire.Codec
* [x/auth] Default TxDecoder can be found in `x/auth` rather than baseapp
* \#1606 The following CLI commands have been switched to use `--from`
* `gaiacli stake create-validator --address-validator`
* `gaiacli stake edit-validator --address-validator`
* `gaiacli stake delegate --address-delegator`
* `gaiacli stake unbond begin --address-delegator`
* `gaiacli stake unbond complete --address-delegator`
* `gaiacli stake redelegate begin --address-delegator`
* `gaiacli stake redelegate complete --address-delegator`
* `gaiacli stake unrevoke [validator-address]`
* `gaiacli gov submit-proposal --proposer`
* `gaiacli gov deposit --depositer`
* `gaiacli gov vote --voter`
* [x/gov] Added tags sub-package, changed tags to use dash-case
* [x/gov] Governance parameters are now stored in globalparams store
* [core] \#1807 Switch from use of rational to decimal
* [lcd] \#1866 Updated lcd /slashing/signing_info endpoint to take cosmosvalpub instead of cosmosvaladdr
* [types] sdk.NewCoin now takes sdk.Int, sdk.NewInt64Coin takes int64
* [cli] #1551: Officially removed `--name` from CLI commands
* [cli] Genesis/key creation (`init`) now supports user-provided key passwords
* [cli] unsafe_reset_all, show_validator, and show_node_id have been renamed to unsafe-reset-all, show-validator, and show-node-id
* [cli] \#1901 Flag --address-validator renamed to --validator in stake and slashing commands
* [types] \#1901 Validator interface's GetOwner() renamed to GetOperator()
* [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface.
* Gaia REST API (`gaiacli advanced rest-server`)
* [x/stake] Validator.Owner renamed to Validator.Operator
* Gaia CLI (`gaiacli`)
* [x/stake] Validator.Owner renamed to Validator.Operator
* [cli] unsafe_reset_all, show_validator, and show_node_id have been renamed to unsafe-reset-all, show-validator, and show-node-id
* [cli] \#1983 --print-response now defaults to true in commands that create and send a transaction
* [cli] \#1983 you can now pass --pubkey or --address to gaiacli keys show to return a plaintext representation of the key's address or public key for use with other commands
* [cli] \#2061 changed proposalID in governance REST endpoints to proposal-id
* [cli] \#2014 `gaiacli advanced` no longer exists - to access `ibc`, `rest-server`, and `validator-set` commands use `gaiacli ibc`, `gaiacli rest-server`, and `gaiacli tendermint`, respectively
* Gaia
* Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013)
* [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface.
* SDK
* [core] \#1807 Switch from use of rational to decimal
* [types] \#1901 Validator interface's GetOwner() renamed to GetOperator()
* Tendermint
FEATURES
* [lcd] Can now query governance proposals by ProposalStatus
* [x/mock/simulation] Randomized simulation framework
* Modules specify invariants and operations, preferably in an x/[module]/simulation package
* Modules can test random combinations of their own operations
* Applications can integrate operations and invariants from modules together for an integrated simulation
* [baseapp] Initialize validator set on ResponseInitChain
* [cosmos-sdk-cli] Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd
* This allows SDK users to initialize a new project repository.
* [tests] Remotenet commands for AWS (awsnet)
* [networks] Added ansible scripts to upgrade seed nodes on a network
* [store] Add transient store
* [gov] Add slashing for validators who do not vote on a proposal
* [cli] added `gov query-proposals` command to CLI. Can filter by `depositer`, `voter`, and `status`
* [core] added BaseApp.Seal - ability to seal baseapp parameters once they've been set
* [scripts] added log output monitoring to DataDog using Ansible scripts
* [gov] added TallyResult type that gets added stored in Proposal after tallying is finished
* Gaia REST API (`gaiacli advanced rest-server`)
* [lcd] Endpoints to query staking pool and params
* Gaia CLI (`gaiacli`)
* [cli] Cmds to query staking pool and params
* [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in
* Gaia
* SDK
* [querier] added custom querier functionality, so ABCI query requests can be handled by keepers
* Tendermint
IMPROVEMENTS
* [baseapp] Allow any alphanumeric character in route
* [cli] Improve error messages for all txs when the account doesn't exist
* [tools] Remove `rm -rf vendor/` from `make get_vendor_deps`
* [x/auth] Recover ErrorOutOfGas panic in order to set sdk.Result attributes correctly
* [x/stake] Add revoked to human-readable validator
* [spec] \#967 Inflation and distribution specs drastically improved
* [tests] Add tests to example apps in docs
* [x/gov] Votes on a proposal can now be queried
* [x/bank] Unit tests are now table-driven
* [tests] Fixes ansible scripts to work with AWS too
* [tests] \#1806 CLI tests are now behind the build flag 'cli_test', so go test works on a new repo
* [x/gov] Initial governance parameters can now be set in the genesis file
* [x/stake] \#1815 Sped up the processing of `EditValidator` txs.
* [server] \#1930 Transactions indexer indexes all tags by default.
* [x/stake] \#2000 Added tests for new staking endpoints
* [x/stake] [#2023](https://github.com/cosmos/cosmos-sdk/pull/2023) Terminate iteration loop in `UpdateBondedValidators` and `UpdateBondedValidatorsFull` when the first revoked validator is encountered and perform a sanity check.
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
* Gaia REST API (`gaiacli advanced rest-server`)
* [x/stake] \#2000 Added tests for new staking endpoints
* Gaia CLI (`gaiacli`)
* [cli] #2060 removed `--select` from `block` command
* Gaia
* [x/stake] [#2023](https://github.com/cosmos/cosmos-sdk/pull/2023) Terminate iteration loop in `UpdateBondedValidators` and `UpdateBondedValidatorsFull` when the first revoked validator is encountered and perform a sanity check.
* [x/auth] Signature verification's gas cost now accounts for pubkey type. [#2046](https://github.com/tendermint/tendermint/pull/2046)
* SDK
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
* Tendermint
BUG FIXES
* \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988)
* \#1666 Add intra-tx counter to the genesis validators
* \#1797 Fix off-by-one error in slashing for downtime
* \#1787 Fixed bug where Tally fails due to revoked/unbonding validator
* \#1766 Fixes bad example for keybase identity
* \#1804 Fixes gen-tx genesis generation logic temporarily until upstream updates
* \#1799 Fix `gaiad export`
* \#1828 Force user to specify amount on create-validator command by removing default
* \#1839 Fixed bug where intra-tx counter wasn't set correctly for genesis validators
* [staking] [#1858](https://github.com/cosmos/cosmos-sdk/pull/1858) Fixed bug where the cliff validator was not be updated correctly
* [tests] \#1675 Fix non-deterministic `test_cover`
* [client] \#1551: Refactored `CoreContext`
* Renamed `CoreContext` to `QueryContext`
* Removed all tx related fields and logic (building & signing) to separate
structure `TxContext` in `x/auth/client/context`
* Cleaned up documentation and API of what used to be `CoreContext`
* Implemented `KeyType` enum for key info
* \#1666 Add intra-tx counter to the genesis validators
* [tests] \#1551: Fixed invalid LCD test JSON payload in `doIBCTransfer`
* \#1787 Fixed bug where Tally fails due to revoked/unbonding validator
* \#1787 Fixed bug where Tally fails due to revoked/unbonding validator
* [basecoin] Fixes coin transaction failure and account query [discussion](https://forum.cosmos.network/t/unmarshalbinarybare-expected-to-read-prefix-bytes-75fbfab8-since-it-is-registered-concrete-but-got-0a141dfa/664/6)
* [cli] \#1997 Handle panics gracefully when `gaiacli stake {delegation,unbond}` fail to unmarshal delegation.
* \#2105 Fix DB Iterator leak, which may leak a go routine.
* Gaia REST API (`gaiacli advanced rest-server`)
* Gaia CLI (`gaiacli`)
* [cli] \#1997 Handle panics gracefully when `gaiacli stake {delegation,unbond}` fail to unmarshal delegation.
* Gaia
* SDK
* \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988)
* \#2105 Fix DB Iterator leak, which may leak a go routine.
* Tendermint

View File

@ -41,13 +41,14 @@ const (
// BaseApp reflects the ABCI application implementation.
type BaseApp struct {
// initialized on creation
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
codespacer *sdk.Codespacer // handle module codespacing
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
queryRouter QueryRouter // router for redirecting query calls
codespacer *sdk.Codespacer // handle module codespacing
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
anteHandler sdk.AnteHandler // ante handler for fee and auth
@ -84,13 +85,14 @@ var _ abci.Application = (*BaseApp)(nil)
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp)) *BaseApp {
app := &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
codespacer: sdk.NewCodespacer(),
txDecoder: txDecoder,
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
queryRouter: NewQueryRouter(),
codespacer: sdk.NewCodespacer(),
txDecoder: txDecoder,
}
// Register the undefined & root codespaces, which should not be used by
@ -266,6 +268,7 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery {
return abci.ResponseQuery{}
}
// Splits a string path using the delimter '/'. i.e. "this/is/funny" becomes []string{"this", "is", "funny"}
func splitPath(requestPath string) (path []string) {
path = strings.Split(requestPath, "/")
// first element is empty string
@ -291,6 +294,8 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
return handleQueryStore(app, path, req)
case "p2p":
return handleQueryP2P(app, path, req)
case "custom":
return handleQueryCustom(app, path, req)
}
msg := "unknown query path"
@ -362,6 +367,33 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc
return sdk.ErrUnknownRequest(msg).QueryResult()
}
func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
// path[0] should be "custom" because "/custom" prefix is required for keeper queries.
// the queryRouter routes using path[1]. For example, in the path "custom/gov/proposal", queryRouter routes using "gov"
if path[1] == "" {
sdk.ErrUnknownRequest("No route for custom query specified").QueryResult()
}
querier := app.queryRouter.Route(path[1])
if querier == nil {
sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult()
}
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger)
// Passes the rest of the path as an argument to the querier.
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
resBytes, err := querier(ctx, path[2:], req)
if err != nil {
return abci.ResponseQuery{
Code: uint32(err.ABCICode()),
Log: err.ABCILog(),
}
}
return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK),
Value: resBytes,
}
}
// BeginBlock implements the ABCI application interface.
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
if app.cms.TracingEnabled() {
@ -379,7 +411,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
} else {
// In the first block, app.deliverState.ctx will already be initialized
// by InitChain. Context is now updated with Header information.
app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header)
app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header).WithBlockHeight(req.Header.Height)
}
if app.beginBlocker != nil {

41
baseapp/queryrouter.go Normal file
View File

@ -0,0 +1,41 @@
package baseapp
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// QueryRouter provides queryables for each query path.
type QueryRouter interface {
AddRoute(r string, h sdk.Querier) (rtr QueryRouter)
Route(path string) (h sdk.Querier)
}
type queryrouter struct {
routes map[string]sdk.Querier
}
// nolint
// NewRouter - create new router
// TODO either make Function unexported or make return type (router) Exported
func NewQueryRouter() *queryrouter {
return &queryrouter{
routes: map[string]sdk.Querier{},
}
}
// AddRoute - Adds an sdk.Querier to the route provided. Panics on duplicate
func (rtr *queryrouter) AddRoute(r string, q sdk.Querier) QueryRouter {
if !isAlphaNumeric(r) {
panic("route expressions can only contain alphanumeric characters")
}
if rtr.routes[r] != nil {
panic("route has already been initialized")
}
rtr.routes[r] = q
return rtr
}
// Returns the sdk.Querier for a certain route path
func (rtr *queryrouter) Route(path string) (h sdk.Querier) {
return rtr.routes[path]
}

View File

@ -74,6 +74,9 @@ func (app *BaseApp) Router() Router {
}
return app.router
}
func (app *BaseApp) QueryRouter() QueryRouter {
return app.queryRouter
}
func (app *BaseApp) Seal() { app.sealed = true }
func (app *BaseApp) IsSealed() bool { return app.sealed }
func (app *BaseApp) enforceSeal() {

View File

@ -31,6 +31,11 @@ func (ctx CLIContext) Query(path string) (res []byte, err error) {
return ctx.query(path, nil)
}
// Query information about the connected node with a data payload
func (ctx CLIContext) QueryWithData(path string, data []byte) (res []byte, err error) {
return ctx.query(path, data)
}
// QueryStore performs a query from a Tendermint node with the provided key and
// store name.
func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) {

View File

@ -52,7 +52,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction")
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
c.Flags().Bool(FlagJson, false, "return output in json format")
c.Flags().Bool(FlagPrintResponse, false, "return tx response (only works with async = false)")
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
}
return cmds
}

View File

@ -4,10 +4,20 @@ import (
"encoding/json"
"net/http"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/pkg/errors"
"github.com/tendermint/tmlibs/cli"
)
const (
// FlagAddress is the flag for the user's address on the command line.
FlagAddress = "address"
// FlagPublicKey represents the user's public key on the command line.
FlagPublicKey = "pubkey"
)
var showKeysCmd = &cobra.Command{
@ -18,13 +28,38 @@ var showKeysCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
info, err := getKey(name)
if err == nil {
printInfo(info)
if err != nil {
return err
}
return err
showAddress := viper.GetBool(FlagAddress)
showPublicKey := viper.GetBool(FlagPublicKey)
outputSet := cmd.Flag(cli.OutputFlag).Changed
if showAddress && showPublicKey {
return errors.New("cannot use both --address and --pubkey at once")
}
if outputSet && (showAddress || showPublicKey) {
return errors.New("cannot use --output with --address or --pubkey")
}
if showAddress {
printKeyAddress(info)
return nil
}
if showPublicKey {
printPubKey(info)
return nil
}
printInfo(info)
return nil
},
}
func init() {
showKeysCmd.Flags().Bool(FlagAddress, false, "output the address only (overrides --output)")
showKeysCmd.Flags().Bool(FlagPublicKey, false, "output the public key only (overrides --output)")
}
func getKey(name string) (keys.Info, error) {
kb, err := GetKeyBase()
if err != nil {

View File

@ -6,7 +6,7 @@ import (
"github.com/spf13/viper"
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/tendermint/tendermint/libs/cli"
dbm "github.com/tendermint/tendermint/libs/db"
@ -173,3 +173,19 @@ func printInfos(infos []keys.Info) {
func printKeyOutput(ko KeyOutput) {
fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey)
}
func printKeyAddress(info keys.Info) {
ko, err := Bech32KeyOutput(info)
if err != nil {
panic(err)
}
fmt.Println(ko.Address.String())
}
func printPubKey(info keys.Info) {
ko, err := Bech32KeyOutput(info)
if err != nil {
panic(err)
}
fmt.Println(ko.PubKey)
}

View File

@ -353,6 +353,42 @@ func TestTxs(t *testing.T) {
require.Equal(t, resultTx.Height, indexedTxs[0].Height)
}
func TestPoolParamsQuery(t *testing.T) {
_, password := "test", "1234567890"
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
defaultParams := stake.DefaultParams()
res, body := Request(t, port, "GET", "/stake/parameters", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var params stake.Params
err := cdc.UnmarshalJSON([]byte(body), &params)
require.Nil(t, err)
require.True(t, defaultParams.Equal(params))
res, body = Request(t, port, "GET", "/stake/pool", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.NotNil(t, body)
initialPool := stake.InitialPool()
initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewDec(100))
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState
initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewDec(int64(50))) // freeFermionsAcc = 50 on GaiaAppGenState
var pool stake.Pool
err = cdc.UnmarshalJSON([]byte(body), &pool)
require.Nil(t, err)
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
require.Equal(t, initialPool.NextInflation(params), pool.Inflation)
initialPool = initialPool.ProcessProvisions(params) // provisions are added to the pool every hour
require.Equal(t, initialPool.LooseTokens, pool.LooseTokens)
}
func TestValidatorsQuery(t *testing.T) {
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()

View File

@ -12,10 +12,6 @@ import (
"github.com/spf13/cobra"
)
const (
flagSelect = "select"
)
//BlockCommand returns the verified block data for a given heights
func BlockCommand() *cobra.Command {
cmd := &cobra.Command{
@ -27,7 +23,6 @@ func BlockCommand() *cobra.Command {
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)")
return cmd
}
@ -38,7 +33,6 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
return nil, err
}
// TODO: actually honor the --select flag!
// header -> BlockchainInfo
// header, tx -> Block
// results -> BlockResults

View File

@ -27,7 +27,20 @@ const (
func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "txs",
Short: "Search for all transactions that match the given tags",
Short: "Search for all transactions that match the given tags.",
Long: strings.TrimSpace(`
Search for transactions that match the given tags. By default, transactions must match ALL tags
passed to the --tags option. To match any transaction, use the --any option.
For example:
$ gaiacli tendermint txs --tag test1,test2
will match any transaction tagged with both test1,test2. To match a transaction tagged with either
test1 or test2, use:
$ gaiacli tendermint txs --tag test1,test2 --any
`),
RunE: func(cmd *cobra.Command, args []string) error {
tags := viper.GetStringSlice(flagTags)
@ -52,7 +65,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
// TODO: change this to false once proofs built in
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)")
cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match")
cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL")
return cmd
}

View File

@ -78,7 +78,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
keyGov: sdk.NewKVStoreKey("gov"),
keyFeeCollection: sdk.NewKVStoreKey("fee"),
keyParams: sdk.NewKVStoreKey("params"),
tkeyParams: sdk.NewTransientStoreKey("params"),
tkeyParams: sdk.NewTransientStoreKey("transient_params"),
}
// define the accountMapper
@ -105,6 +105,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
AddRoute("gov", gov.NewHandler(app.govKeeper))
app.QueryRouter().
AddRoute("gov", gov.NewQuerier(app.govKeeper))
// initialize BaseApp
app.SetInitChainer(app.initChainer)
app.SetBeginBlocker(app.BeginBlocker)
@ -185,7 +188,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// load the address to pubkey map
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState())
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
return abci.ResponseInitChain{
Validators: validators,
@ -208,6 +211,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
GovData: gov.WriteGenesis(ctx, app.govKeeper),
}
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {

View File

@ -11,6 +11,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/spf13/pflag"
@ -32,6 +33,7 @@ var (
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
GovData gov.GenesisState `json:"gov"`
}
// GenesisAccount doesn't need pubkey or sequence
@ -216,6 +218,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
GovData: gov.DefaultGenesisState(),
}
return
}

View File

@ -3,52 +3,74 @@ package app
import (
"encoding/json"
"flag"
"fmt"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
stake "github.com/cosmos/cosmos-sdk/x/stake"
stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation"
)
var (
seed int64
numKeys int
numBlocks int
blockSize int
enabled bool
verbose bool
)
func init() {
flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed")
flag.IntVar(&numKeys, "SimulationNumKeys", 10, "Number of keys (accounts)")
flag.IntVar(&numBlocks, "SimulationNumBlocks", 100, "Number of blocks")
flag.IntVar(&blockSize, "SimulationBlockSize", 100, "Operations per block")
flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "Number of blocks")
flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block")
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
}
func appStateFn(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage {
func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
var genesisAccounts []GenesisAccount
// Randomly generate some genesis accounts
for _, addr := range accs {
for _, acc := range accs {
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}}
genesisAccounts = append(genesisAccounts, GenesisAccount{
Address: addr,
Address: acc,
Coins: coins,
})
}
// Default genesis state
stakeGenesis := stake.DefaultGenesisState()
stakeGenesis.Pool.LooseTokens = sdk.NewDec(1000)
var validators []stake.Validator
var delegations []stake.Delegation
// XXX Try different numbers of initially bonded validators
numInitiallyBonded := int64(50)
for i := 0; i < int(numInitiallyBonded); i++ {
validator := stake.NewValidator(accs[i], keys[i].PubKey(), stake.Description{})
validator.Tokens = sdk.NewDec(100)
validator.DelegatorShares = sdk.NewDec(100)
delegation := stake.Delegation{accs[i], accs[i], sdk.NewDec(100), 0}
validators = append(validators, validator)
delegations = append(delegations, delegation)
}
stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(100*250) + (numInitiallyBonded * 100))
stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations
// No inflation, for now
stakeGenesis.Params.InflationMax = sdk.NewDec(0)
stakeGenesis.Params.InflationMin = sdk.NewDec(0)
genesis := GenesisState{
Accounts: genesisAccounts,
StakeData: stakeGenesis,
@ -63,13 +85,46 @@ func appStateFn(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage {
return appState
}
func testAndRunTxs(app *GaiaApp) []simulation.TestAndRunTx {
return []simulation.TestAndRunTx{
banksim.TestAndRunSingleInputMsgSend(app.accountMapper),
govsim.SimulateMsgSubmitProposal(app.govKeeper, app.stakeKeeper),
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
govsim.SimulateMsgVote(app.govKeeper, app.stakeKeeper),
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper),
stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper),
slashingsim.SimulateMsgUnrevoke(app.slashingKeeper),
}
}
func invariants(app *GaiaApp) []simulation.Invariant {
return []simulation.Invariant{
func(t *testing.T, baseapp *baseapp.BaseApp, log string) {
banksim.NonnegativeBalanceInvariant(app.accountMapper)(t, baseapp, log)
govsim.AllInvariants()(t, baseapp, log)
stakesim.AllInvariants(app.coinKeeper, app.stakeKeeper, app.accountMapper)(t, baseapp, log)
slashingsim.AllInvariants()(t, baseapp, log)
},
}
}
func TestFullGaiaSimulation(t *testing.T) {
if !enabled {
t.Skip("Skipping Gaia simulation")
}
// Setup Gaia application
logger := log.NewNopLogger()
var logger log.Logger
if verbose {
logger = log.TestingLogger()
} else {
logger = log.NewNopLogger()
}
db := dbm.NewMemDB()
app := NewGaiaApp(logger, db, nil)
require.Equal(t, "GaiaApp", app.Name())
@ -77,24 +132,50 @@ func TestFullGaiaSimulation(t *testing.T) {
// Run randomized simulation
simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
[]simulation.TestAndRunTx{
banksim.TestAndRunSingleInputMsgSend(app.accountMapper),
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper),
stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper),
stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper),
},
testAndRunTxs(app),
[]simulation.RandSetup{},
[]simulation.Invariant{
banksim.NonnegativeBalanceInvariant(app.accountMapper),
stakesim.AllInvariants(app.coinKeeper, app.stakeKeeper, app.accountMapper),
},
numKeys,
invariants(app),
numBlocks,
blockSize,
false,
)
}
// TODO: Make another test for the fuzzer itself, which just has noOp txs
// and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) {
if !enabled {
t.Skip("Skipping Gaia simulation")
}
numSeeds := 5
numTimesToRunPerSeed := 5
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
for i := 0; i < numSeeds; i++ {
seed := rand.Int63()
for j := 0; j < numTimesToRunPerSeed; j++ {
logger := log.NewNopLogger()
db := dbm.NewMemDB()
app := NewGaiaApp(logger, db, nil)
// Run randomized simulation
simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app),
[]simulation.RandSetup{},
[]simulation.Invariant{},
20,
20,
true,
)
appHash := app.LastCommitID().Hash
fmt.Printf(">>> APP HASH: %v, %X\n", appHash, appHash)
appHashList[j] = appHash
}
for k := 1; k < numTimesToRunPerSeed; k++ {
require.Equal(t, appHashList[0], appHashList[k])
}
}
}

View File

@ -117,6 +117,11 @@ func TestGaiaCLICreateValidator(t *testing.T) {
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
defaultParams := stake.DefaultParams()
initialPool := stake.InitialPool()
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState
initialPool = initialPool.ProcessProvisions(defaultParams) // provisions are added to the pool every hour
// create validator
cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags)
cvStr += fmt.Sprintf(" --from=%s", "bar")
@ -124,6 +129,8 @@ func TestGaiaCLICreateValidator(t *testing.T) {
cvStr += fmt.Sprintf(" --amount=%v", "2steak")
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1))
executeWrite(t, cvStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
@ -150,6 +157,14 @@ func TestGaiaCLICreateValidator(t *testing.T) {
*/
validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags))
require.Equal(t, "1.0000000000", validator.Tokens.String())
params := executeGetParams(t, fmt.Sprintf("gaiacli stake parameters --output=json %v", flags))
require.True(t, defaultParams.Equal(params))
pool := executeGetPool(t, fmt.Sprintf("gaiacli stake pool --output=json %v", flags))
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
}
func TestGaiaCLISubmitProposal(t *testing.T) {
@ -328,6 +343,9 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
return acc
}
//___________________________________________________________________________________
// stake
func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
out := tests.ExecuteT(t, cmdStr, "")
var validator stake.Validator
@ -337,6 +355,27 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
return validator
}
func executeGetPool(t *testing.T, cmdStr string) stake.Pool {
out := tests.ExecuteT(t, cmdStr, "")
var pool stake.Pool
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &pool)
require.NoError(t, err, "out %v\n, err %v", out, err)
return pool
}
func executeGetParams(t *testing.T, cmdStr string) stake.Params {
out := tests.ExecuteT(t, cmdStr, "")
var params stake.Params
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &params)
require.NoError(t, err, "out %v\n, err %v", out, err)
return params
}
//___________________________________________________________________________________
// gov
func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
out := tests.ExecuteT(t, cmdStr, "")
var proposal gov.Proposal

View File

@ -62,18 +62,10 @@ func main() {
ibccmd.IBCRelayCmd(cdc),
)...)
advancedCmd := &cobra.Command{
Use: "advanced",
Short: "Advanced subcommands",
}
advancedCmd.AddCommand(
rootCmd.AddCommand(
tendermintCmd,
ibcCmd,
lcd.ServeCommand(cdc),
)
rootCmd.AddCommand(
advancedCmd,
client.LineBreak,
)
@ -88,6 +80,12 @@ func main() {
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
stakecmd.GetCmdQueryParams("stake", cdc),
stakecmd.GetCmdQueryPool("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc),
stakecmd.GetCmdQueryRedelegation("stake", cdc),
stakecmd.GetCmdQueryRedelegations("stake", cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
)...)
stakeCmd.AddCommand(

View File

@ -1,20 +0,0 @@
{
"node_id": "1ebc5ca705b3ae1c06a0888ff1287ada82149dc3",
"ip": "138.68.77.24",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4="
},
"power": 100,
"name": "adrian"
},
"app_gen_tx": {
"name": "default",
"address": "D9C12CB5186FE0018179742FD3110EE534C63460",
"pub_key": {
"type": "AC26791624DE60",
"value": "TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "c272ae3cff7558db2c6195eea38fd43fd08406dc",
"ip": "206.189.31.178",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "tJlZJWjOpYvRitYFTWNPTaUtvQVf+hoNjlfI84VPqvI="
},
"power": 100,
"name": "anton"
},
"app_gen_tx": {
"name": "default",
"address": "E766088FD171906289617F60BF0014C46F0F85EC",
"pub_key": {
"type": "AC26791624DE60",
"value": "tJlZJWjOpYvRitYFTWNPTaUtvQVf+hoNjlfI84VPqvI="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "aef085c4bfed0c1ffc6705f2e1e3bf85e5164600",
"ip": "45.77.53.208",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "RpX+xkwnCNw5DpBelscz4//TiODyC9RDiyIuD6NEwx0="
},
"power": 100,
"name": "aurel"
},
"app_gen_tx": {
"name": "aurel",
"address": "10B0899E05A486AE4E5589C39587DF7E9A185872",
"pub_key": {
"type": "AC26791624DE60",
"value": "RpX+xkwnCNw5DpBelscz4//TiODyC9RDiyIuD6NEwx0="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "b0dd378c3fbc4c156cd6d302a799f0d2e4227201",
"ip": "159.89.121.174",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "0aNTDL49987ZNRi3FtJIi0jk93ybHuYg1FjWrfP9H2o="
},
"power": 100,
"name": "bucky"
},
"app_gen_tx": {
"name": "bucky",
"address": "935E48ED79F1006ED135553768E1D9A768747CF6",
"pub_key": {
"type": "AC26791624DE60",
"value": "0aNTDL49987ZNRi3FtJIi0jk93ybHuYg1FjWrfP9H2o="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "e25603602d8cf8542570ad0e311d50f55f497f85",
"ip": "158.69.63.13",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "dcmCn+RZTBdwbCa4YqSnw/Va7xQloBw6vF87ItLwdM0="
},
"power": 100,
"name": "cwgoes"
},
"app_gen_tx": {
"name": "cwgoes",
"address": "328FBB8EA315D070DF908982A5F91A3618001D20",
"pub_key": {
"type": "AC26791624DE60",
"value": "dcmCn+RZTBdwbCa4YqSnw/Va7xQloBw6vF87ItLwdM0="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "aabf05a67b2f399807dc602d05bf97b0ed283ac2",
"ip": "116.62.62.39",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "7SaH/LyM+qdz9ovD/pvqIf2q7LC7tc5v0ZJxsA2CGTw="
},
"power": 100,
"name": "iris"
},
"app_gen_tx": {
"name": "=suyu",
"address": "4B5BE759EB23B0D76C6A60636BD0E3111178794E",
"pub_key": {
"type": "AC26791624DE60",
"value": "7SaH/LyM+qdz9ovD/pvqIf2q7LC7tc5v0ZJxsA2CGTw="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "79466a03e9d4b4648a7dd8cead1fa7121ce76ee3",
"ip": "34.235.130.1",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "SW12+WpGKUCO9oT2CV0CD5kUclbXjJHV1MjerLWB7Oc="
},
"power": 100,
"name": "lino"
},
"app_gen_tx": {
"name": "lino",
"address": "5A007B81A25AF34B829B79DA508A26E12180BCDB",
"pub_key": {
"type": "AC26791624DE60",
"value": "SW12+WpGKUCO9oT2CV0CD5kUclbXjJHV1MjerLWB7Oc="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "adb290585a2753bf1a520c76802b0dab3dffa895",
"ip": "34.201.21.179",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "pY7eLF0Ez3yq495kIjag8mD67Q131np/ssagpEvlV2A="
},
"power": 100,
"name": "pbostrom"
},
"app_gen_tx": {
"name": "default",
"address": "109720515B4F8C0858DA3521E448262334534FFD",
"pub_key": {
"type": "AC26791624DE60",
"value": "pY7eLF0Ez3yq495kIjag8mD67Q131np/ssagpEvlV2A="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "678503e6c8f50db7279c7da3cb9b072aac4bc0d5",
"ip": "35.193.188.125",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "RMwWTZsVdkq1heicNJb2fosy9Fls4NHxAHReiJvHl+8="
},
"power": 100,
"name": "polsdam"
},
"app_gen_tx": {
"name": "poldsam",
"address": "FA929191B04C5DB222AFC6F15C63EF48CCC864C5",
"pub_key": {
"type": "AC26791624DE60",
"value": "RMwWTZsVdkq1heicNJb2fosy9Fls4NHxAHReiJvHl+8="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "3519f05985394107e0b2e285361b7e012adb1113",
"ip": "54.209.118.64",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "vq0V0BjpmIh6WyNnFpMaO5LyUK2FamkNt65eJYa5AaQ="
},
"power": 100,
"name": "staked"
},
"app_gen_tx": {
"name": "default",
"address": "935E04662697134905706A4CCDB822AC6FC11C2E",
"pub_key": {
"type": "AC26791624DE60",
"value": "vq0V0BjpmIh6WyNnFpMaO5LyUK2FamkNt65eJYa5AaQ="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "8a2802fb25d352f3e7e277559a4f683780c3ef22",
"ip": "167.99.191.184",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "NjjEQKUsq8F0gWxl3BoU2Li5n7hEz9H/LX80rfMxVyE="
},
"power": 100,
"name": ""
},
"app_gen_tx": {
"name": "zach",
"address": "9D5723057702E2090405AB5D3B48C45B9ABF4377",
"pub_key": {
"type": "AC26791624DE60",
"value": "NjjEQKUsq8F0gWxl3BoU2Li5n7hEz9H/LX80rfMxVyE="
}
}
}

View File

@ -1,20 +0,0 @@
{
"node_id": "30b45459e4881680c0ef1750fde136fefa6c3b98",
"ip": "35.184.182.143",
"validator": {
"pub_key": {
"type": "AC26791624DE60",
"value": "CDF/8aD8Lt+ikR3LyCg9c7DwWBA51NH+MUkH7tzxrfY="
},
"power": 100,
"name": "zaki"
},
"app_gen_tx": {
"name": "zaki",
"address": "ECE57661F0CDCF28EED257B72F86240E57F4A612",
"pub_key": {
"type": "AC26791624DE60",
"value": "CDF/8aD8Lt+ikR3LyCg9c7DwWBA51NH+MUkH7tzxrfY="
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,6 @@
- [ ] 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 `{{ .Release.Name }}-rcN`
- [ ] 6. Kick off 1 day of automated fuzz testing
- [ ] 7. Release Lead assigns 2 people to perform buddy testing script
- [ ] 7. Release Lead assigns 2 people to perform [buddy testing script](/docs/RELEASE_TEST_SCRIPT.md) and update the relevant documentation
- [ ] 8. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`)
- [ ] 9. After #6 and #7 have successfully completed then merge the release PR and push the final release tag

View File

@ -0,0 +1,17 @@
This document should contain plain english instructions for testing functionality on `gaiad`. This “Script” is supposed to be run by 2 people who will each spin up a `gaiad` node and run the series of prompts below.
- [Create a network of 2 nodes](getting-started/create-testnet.md)
- [Generate an account](sdk/clients.md)
- [Send funds from one account to the other](sdk/clients.md)
- [Create a validator](validators/validator-setup.md)
- [Edit a validator](validators/validator-setup.md)
- [Delegate to validator](sdk/clients.md)
- [Unbond from a validator](sdk/clients.md)
- [View validators and verify output](validators/validator-setup.md)
- [Query network status](getting-started/full-node.md)
- [Create a proposal](validators/validator-setup.md)
- [Query a proposal](validators/validator-setup.md)
- [Vote on a proposal](validators/validator-setup.md)
- [Query status of a proposal](validators/validator-setup.md)
- [Query the votes on a proposal](validators/validator-setup.md)
- [Export state and reload](getting-started/create-testnet.md)

View File

@ -2,7 +2,7 @@ swagger: '2.0'
info:
version: '1.1.0'
title: Gaia-Lite (former LCD) to interface with Cosmos BaseServer via REST
description: Specification for Gaia-lite provided by `gaiacli advanced rest-server`
description: Specification for Gaia-lite provided by `gaiacli rest-server`
tags:
- name: keys

View File

@ -1,6 +1,6 @@
# REST
See `gaiacli advanced rest-server --help` for more.
See `gaiacli rest-server --help` for more.
Also see the
[work in progress API specification](https://github.com/cosmos/cosmos-sdk/pull/1314)

View File

@ -0,0 +1,27 @@
## Create your Own Testnet
To create your own testnet, first each validator will need to install gaiad and run gen-tx
```bash
gaiad init gen-tx --name <account_name>
```
This populations `$HOME/.gaiad/gen-tx/` with a json file.
Now these json files need to be aggregated together via Github, a Google form, pastebin or other methods.
Place all files on one computer in `$HOME/.gaiad/gen-tx/`
```bash
gaiad init --gen-txs -o --chain=<chain-name>
```
This will generate a `genesis.json` in `$HOME/.gaiad/config/genesis.json` distribute this file to all validators on your testnet.
### Export state
To export state and reload (useful for testing purposes):
```
gaiad export > genesis.json; cp genesis.json ~/.gaiad/config/genesis.json; gaiad start
```

View File

@ -0,0 +1,193 @@
# ICS 030: Cosmos Signed Messages
>TODO: Replace with valid ICS number and possibly move to new location.
* [Changelog](#changelog)
* [Abstract](#abstract)
* [Preliminary](#preliminary)
* [Specification](#specification)
* [Future Adaptations](#future-adaptations)
* [API](#api)
* [References](#references)
## Status
Proposed.
## Changelog
## Abstract
Having the ability to sign messages off-chain has proven to be a fundamental aspect
of nearly any blockchain. The notion of signing messages off-chain has many
added benefits such as saving on computational costs and reducing transaction
throughput and overhead. Within the context of the Cosmos, some of the major
applications of signing such data includes, but is not limited to, providing a
cryptographic secure and verifiable means of proving validator identity and
possibly associating it with some other framework or organization. In addition,
having the ability to sign Cosmos messages with a Ledger or similar HSM device.
A standardized protocol for hashing, signing, and verifying messages that can be
implemented by the Cosmos SDK and other third-party organizations is needed. Such a
standardized protocol subscribes to the following:
* Contains a specification of human-readable and machine-verifiable typed structured data
* Contains a framework for deterministic and injective encoding of structured data
* Utilizes cryptographic secure hashing and signing algorithms
* A framework for supporting extensions and domain separation
* Is invulnerable to chosen ciphertext attacks
* Has protection against potentially signing transactions a user did not intend to
This specification is only concerned with the rationale and the standardized
implementation of Cosmos signed messages. It does **not** concern itself with the
concept of replay attacks as that will be left up to the higher-level application
implementation. If you view signed messages in the means of authorizing some
action or data, then such an application would have to either treat this as
idempotent or have mechanisms in place to reject known signed messages.
## Preliminary
The Cosmos message signing protocol will be parameterized with a cryptographic
secure hashing algorithm `SHA-256` and a signing algorithm `S` that contains
the operations `sign` and `verify` which provide a digital signature over a set
of bytes and verification of a signature respectively.
Note, our goal here is not to provide context and reasoning about why necessarily
these algorithms were chosen apart from the fact they are the defacto algorithms
used in Tendermint and the Cosmos SDK and that they satisfy our needs for such
cryptographic algorithms such as having resistance to collision and second
pre-image attacks, as well as being [deterministic](https://en.wikipedia.org/wiki/Hash_function#Determinism) and [uniform](https://en.wikipedia.org/wiki/Hash_function#Uniformity).
## Specification
Tendermint has a well established protocol for signing messages using a canonical
JSON representation as defined [here](https://github.com/tendermint/tendermint/blob/master/types/canonical_json.go).
An example of such a canonical JSON structure is Tendermint's vote structure:
```golang
type CanonicalJSONVote struct {
ChainID string `json:"@chain_id"`
Type string `json:"@type"`
BlockID CanonicalJSONBlockID `json:"block_id"`
Height int64 `json:"height"`
Round int `json:"round"`
Timestamp string `json:"timestamp"`
VoteType byte `json:"type"`
}
```
With such canonical JSON structures, the specification requires that they include
meta fields: `@chain_id` and `@type`. These meta fields are reserved and must be
included. They are both of type `string`. In addition, fields must be ordered
in lexicographically ascending order.
For the purposes of signing Cosmos messages, the `@chain_id` field must correspond
to the Cosmos chain identifier. The user-agent should **refuse** signing if the
`@chain_id` field does not match the currently active chain! The `@type` field
must equal the constant `"message"`. The `@type` field corresponds to the type of
structure the user will be signing in an application. For now, a user is only
allowed to sign bytes of valid ASCII text ([see here](https://github.com/tendermint/tendermint/blob/master/libs/common/string.go#L61-L74)).
However, this will change and evolve to support additional application-specific
structures that are human-readable and machine-verifiable ([see Future Adaptations](#futureadaptations)).
Thus, we can have a canonical JSON structure for signing Cosmos messages using
the [JSON schema](http://json-schema.org/) specification as such:
```json
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "cosmos/signing/typeData/schema",
"title": "The Cosmos signed message typed data schema.",
"type": "object",
"properties": {
"@chain_id": {
"type": "string",
"description": "The corresponding Cosmos chain identifier.",
"minLength": 1
},
"@type": {
"type": "string",
"description": "The message type. It must be 'message'.",
"enum": [
"message"
]
},
"text": {
"type": "string",
"description": "The valid ASCII text to sign.",
"pattern": "^[\\x20-\\x7E]+$",
"minLength": 1
}
},
"required": [
"@chain_id",
"@type",
"text"
]
}
```
e.g.
```json
{
"@chain_id": "1",
"@type": "message",
"text": "Hello, you can identify me as XYZ on keybase."
}
```
## Future Adaptations
As applications can vary greatly in domain, it will be vital to support both
domain separation and human-readable and machine-verifiable structures.
Domain separation will allow for application developers to prevent collisions of
otherwise identical structures. It should be designed to be unique per application
use and should directly be used in the signature encoding itself.
Human-readable and machine-verifiable structures will allow end users to sign
more complex structures, apart from just string messages, and still be able to
know exactly what they are signing (opposed to signing a bunch of arbitrary bytes).
Thus, in the future, the Cosmos signing message specification will be expected
to expand upon it's canonical JSON structure to include such functionality.
## API
Application developers and designers should formalize a standard set of APIs that
adhere to the following specification:
-----
### **cosmosSignBytes**
Params:
* `data`: the Cosmos signed message canonical JSON structure
* `address`: the Bech32 Cosmos account address to sign data with
Returns:
* `signature`: the Cosmos signature derived using signing algorithm `S`
-----
### Examples
Using the `secp256k1` as the DSA, `S`:
```javascript
data = {
"@chain_id": "1",
"@type": "message",
"text": "I hereby claim I am ABC on Keybase!"
}
cosmosSignBytes(data, "cosmosaccaddr1pvsch6cddahhrn5e8ekw0us50dpnugwnlfngt3")
> "0x7fc4a495473045022100dec81a9820df0102381cdbf7e8b0f1e2cb64c58e0ecda1324543742e0388e41a02200df37905a6505c1b56a404e23b7473d2c0bc5bcda96771d2dda59df6ed2b98f8"
```
## References

File diff suppressed because it is too large Load Diff

View File

@ -12,27 +12,25 @@
`gaiacli` is the command line interface to manage accounts and transactions on Cosmos testnets. Here is a list of useful `gaiacli` commands, including usage examples.
### Key Types
### Keys
#### Key Types
There are three types of key representations that are used:
- `cosmosaccaddr`
- Derived from account keys generated by `gaiacli keys add`
- Used to receive funds
- e.g. `cosmosaccaddr15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc`
- `cosmosaccpub`
- Derived from account keys generated by `gaiacli keys add`
- e.g. `cosmosaccpub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm`
- `cosmosvalpub`
- Generated when the node is created with `gaiad init`.
- Get this value with `gaiad tendermint show-validator`
- e.g. `cosmosvalpub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c`
### Generate Keys
#### Generate Keys
You'll need an account private and public key pair \(a.k.a. `sk, pk` respectively\) to be able to receive funds, send txs, bond tx, etc.
@ -66,10 +64,14 @@ gaiad tendermint show-validator
We strongly recommend _NOT_ using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds.
:::
### Get Tokens
### Account
#### Get Tokens
The best way to get tokens is from the [Cosmos Testnet Faucet](https://faucetcosmos.network). If the faucet is not working for you, try asking [#cosmos-validators](https://riot.im/app/#/room/#cosmos-validators:matrix.org). The faucet needs the `cosmosaccaddr` from the account you wish to use for staking.
#### Query Account balance
After receiving tokens to your address, you can view your account's balance by typing:
```bash
@ -79,7 +81,6 @@ gaiacli account <account_cosmosaccaddr>
::: warning Note
When you query an account balance with zero tokens, you will get this error: `No account with address <account_cosmosaccaddr> was found in the state.` This can also happen if you fund the account before your node has fully synced with the chain. These are both normal.
We're working on improving our error messages!
:::
### Send Tokens
@ -87,7 +88,7 @@ We're working on improving our error messages!
```bash
gaiacli send \
--amount=10faucetToken \
--chain-id=gaia-7005 \
--chain-id=<chain_id> \
--name=<key_name> \
--to=<destination_cosmosaccaddr>
```
@ -109,20 +110,40 @@ You can also check your balance at a given block by using the `--block` flag:
gaiacli account <account_cosmosaccaddr> --block=<block_height>
```
### Delegate
### Staking
#### Set up a Validator
Please refer to the [Validator Setup](https://cosmos.network/docs/validators/validator-setup.html) section for a more complete guide on how to set up a validator-candidate.
#### Delegate to a Validator
On the upcoming mainnet, you can delegate `atom` to a validator. These [delegators](/resources/delegators-faq) can receive part of the validator's fee revenue. Read more about the [Cosmos Token Model](https://github.com/cosmos/cosmos/raw/master/Cosmos_Token_Model.pdf).
### Bond Tokens
##### Query Validators
On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond tokens to a testnet validator:
You can query the list of all validators of a specific chain:
```bash
gaiacli stake validators
```
If you want to get the information of a single validator you can check it with:
```bash
gaiacli stake validator <account_cosmosaccaddr>
```
#### Bond Tokens
On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond tokens to a testnet validator (*i.e.* delegate):
```bash
gaiacli stake delegate \
--amount=10steak \
--validator=$(gaiad tendermint show-validator) \
--name=<key_name> \
--chain-id=gaia-6002
--chain-id=<chain_id>
```
While tokens are bonded, they are pooled with all the other bonded tokens in the network. Validators and delegators obtain a percentage of shares that equal their stake in this pool.
@ -131,33 +152,232 @@ While tokens are bonded, they are pooled with all the other bonded tokens in the
Don't use more `steak` thank you have! You can always get more by using the [Faucet](https://faucetcosmos.network/)!
:::
### Unbond Tokens
##### Query Delegations
If for any reason the validator misbehaves, or you want to unbond a certain amount of tokens, use this following command. You can unbond a specific amount of`shares`\(eg:`12.1`\) or all of them \(`MAX`\).
Once submitted a delegation to a validator, you can see it's information by using the following command:
```bash
gaiacli stake delegation \
--address-delegator=<account_cosmosaccaddr> \
--address-validator=$(gaiad tendermint show-validator)
```
Or if you want to check all your current delegations with disctinct validators:
```bash
gaiacli stake delegations <account_cosmosaccaddr>
```
You can also get previous delegation(s) status by adding the `--height` flag.
#### Unbond Tokens
If for any reason the validator misbehaves, or you just want to unbond a certain amount of tokens, use this following command. You can unbond a specific `shares-amount` (eg:`12.1`\) or a `shares-percent` (eg:`25`) with the corresponding flags.
```bash
gaiacli stake unbond begin \
--validator=$(gaiad tendermint show-validator) \
--shares=MAX \
--address-validator=$(gaiad tendermint show-validator) \
--shares-percent=100 \
--from=<key_name> \
--chain-id=gaia-7005
--chain-id=<chain_id>
```
Later you must use the `gaiacli stake unbond complete` command to finish
unbonding at which point you can can check your balance and your stake
delegation to see that the unbonding went through successfully.
Later you must complete the unbonding process by using the `gaiacli stake unbond complete` command:
```bash
gaiacli account <account_cosmosaccaddr>
gaiacli stake delegation \
--address-delegator=<account_cosmosaccaddr> \
--validator=$(gaiad tendermint show-validator) \
--chain-id=gaia-7005
gaiacli stake unbond complete \
--address-validator=$(gaiad tendermint show-validator) \
--from=<key_name> \
--chain-id=<chain_id>
```
## Light Client Daemon
##### Query Unbonding-Delegations
Once you begin an unbonding-delegation, you can see it's information by using the following command:
```bash
gaiacli stake unbonding-delegation \
--address-delegator=<account_cosmosaccaddr> \
--address-validator=$(gaiad tendermint show-validator) \
```
Or if you want to check all your current unbonding-delegations with disctinct validators:
```bash
gaiacli stake unbonding-delegations <account_cosmosaccaddr>
```
You can also get previous unbonding-delegation(s) status by adding the `--height` flag.
#### Redelegate Tokens
A redelegation is a type delegation that allows you to bond illiquid tokens from one validator to another:
```bash
gaiacli stake redelegate begin \
--address-validator-source=$(gaiad tendermint show-validator) \
--address-validator-dest=<account_cosmosaccaddr> \
--shares-percent=50 \
--from=<key_name> \
--chain-id=<chain_id>
```
Here you can also redelegate a specific `shares-amount` or a `shares-percent` with the corresponding flags.
Later you must complete the redelegation process by using the `gaiacli stake redelegate complete` command:
```bash
gaiacli stake unbond complete \
--address-validator=$(gaiad tendermint show-validator) \
--from=<key_name> \
--chain-id=<chain_id>
```
##### Query Redelegations
Once you begin an redelegation, you can see it's information by using the following command:
```bash
gaiacli stake redelegation \
--address-delegator=<account_cosmosaccaddr> \
--address-validator-source=$(gaiad tendermint show-validator) \
--address-validator-dest=<account_cosmosaccaddr> \
```
Or if you want to check all your current unbonding-delegations with disctinct validators:
```bash
gaiacli stake redelegations <account_cosmosaccaddr>
```
You can also get previous redelegation(s) status by adding the `--height` flag.
### Governance
Governance is the process from which users in the Cosmos Hub can come to consensus on software upgrades, parameters of the mainnet or on custom text proposals. This is done through voting on proposals, which will be submitted by `Atom` holders on the mainnet.
Some considerations about the voting process:
- Voting is done by bonded `Atom` holders on a 1 bonded `Atom` 1 vote basis
- Delegators inherit the vote of their validator if they don't vote
- **Validators MUST vote on every proposal**. If a validator does not vote on a proposal, they will be **partially slashed**
- Votes are tallied at the end of the voting period (2 weeks on mainnet). Each address can vote multiple times to update its `Option` value (paying the transaction fee each time), only the last casted vote will count as valid
- Voters can choose between options `Yes`, `No`, `NoWithVeto` and `Abstain`
At the end of the voting period, a proposal is accepted if `(YesVotes/(YesVotes+NoVotes+NoWithVetoVotes))>1/2` and `(NoWithVetoVotes/(YesVotes+NoVotes+NoWithVetoVotes))<1/3`. It is rejected otherwise
For more information about the governance process and how it works, please check out the Governance module [specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/governance).
#### Create a Governance proposal
In order to create a governance proposal, you must submit an initial deposit along with the proposal details:
- `title`: Title of the proposal
- `description`: Description of the proposal
- `type`: Type of proposal. Must be of value _Text_ (types _SoftwareUpgrade_ and _ParameterChange_ not supported yet).
```bash
gaiacli gov submit-proposal \
--title=<title> \
--description=<description> \
--type=<Text/ParameterChange/SoftwareUpgrade> \
--proposer=<account_cosmosaccaddr> \
--deposit=<40steak> \
--from=<name> \
--chain-id=<chain_id>
```
##### Query proposals
Once created, you can now query information of the proposal:
```bash
gaiacli gov query-proposal \
--proposal-id=<proposal_id>
```
Or query all available proposals:
```bash
gaiacli gov query-proposals
```
You can also query proposals filtered by `voter` or `depositer` by using the corresponding flags.
#### Increase deposit
In order for a proposal to be broadcasted to the network, the amount deposited must be above a `minDeposit` value (default: `10 steak`). If the proposal you previously created didn't meet this requirement, you can still increase the total amount deposited to activate it. Once the minimum deposit is reached, the proposal enters voting period:
```bash
gaiacli gov deposit \
--proposal-id=<proposal_id> \
--depositer=<account_cosmosaccaddr> \
--deposit=<200steak> \
--from=<name> \
--chain-id=<chain_id>
```
> _NOTE_: Proposals that don't meet this requirement will be deleted after `MaxDepositPeriod` is reached.
#### Vote on a proposal
After a proposal's deposit reaches the `MinDeposit` value, the voting period opens. Bonded `Atom` holders can then cast vote on it:
```bash
gaiacli gov vote \
--proposal-id=<proposal_id> \
--voter=<account_cosmosaccaddr> \
--option=<Yes/No/NoWithVeto/Abstain> \
--from=<name> \
--chain-id=<chain_id>
```
##### Query vote
Check the vote with the option you just submitted:
```bash
gaiacli gov query-vote \
--proposal-id=<proposal_id> \
--voter=<account_cosmosaccaddr>
```
#### Query Parameters
You can get the current parameters that define high level settings for staking:
```
gaiacli stake parameters
```
With the above command you will get the values for:
- Maximum and minumum Inflation rate
- Maximum annual change in inflation rate,
- Goal of bonded tokens (%)
- Unbonding time
- Maximum numbers of validators
- Coin denomination for staking
All this values can be updated though a `governance` process by submitting a parameter change `proposal`.
#### Query Pool
A staking `Pool` defines the dynamic parameters of the current state. You can query them with the following command:
```
gaiacli stake pool
```
With the `pool` command you will get the values for:
- Loose and bonded tokens
- Token supply
- Current anual inflation and the block in which the last inflation was processed
- Last recorded bonded shares
## Gaia-Lite
::: tip Note
🚧 We are actively working on documentation for the LCD.
🚧 We are actively working on documentation for Gaia-lite.
:::

View File

@ -6,7 +6,7 @@
The Cosmos SDK has all the necessary pre-built modules to add functionality on top of a `BaseApp`, which is the template to build a blockchain dApp in Cosmos. In this context, a `module` is a fundamental unit in the Cosmos SDK.
Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, as well as REST and CLI for secure user interactions.
Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, queriers for handling query requests, as well as REST and CLI for secure user interactions.
Some of the most important modules in the SDK are:

View File

@ -6,6 +6,23 @@ Uuse the CLI to create a new proposal:
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
```
Or, via a json file:
```bash
simplegovcli propose --proposal="path/to/proposal.json"
```
Where proposal.json contains:
```json
{
"title": "Voting Period Update",
"description": "Should we change the proposal voting period to 3 weeks?",
"type": "Text",
"deposit": "300Atoms"
}
```
Get the details of your newly created proposal:
```bash

159
docs/spec/auth/vesting.md Normal file
View File

@ -0,0 +1,159 @@
## Vesting
### Intro and Requirements
This paper specifies vesting account implementation for the Cosmos Hub.
The requirements for this vesting account is that it should be initialized during genesis with
a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators
and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked.
The vesting account should also be able to spend any coins it receives from other users.
Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their
unlocked coin amount.
### Implementation
##### Vesting Account implementation
NOTE: `Now = ctx.BlockHeader().Time`
```go
type VestingAccount interface {
Account
AssertIsVestingAccount() // existence implies that account is vesting.
// Calculates amount of coins that can be sent to other accounts given the current time
SendableCoins(sdk.Context) sdk.Coins
}
// Implements Vesting Account
// Continuously vests by unlocking coins linearly with respect to time
type ContinuousVestingAccount struct {
BaseAccount
OriginalVestingCoins sdk.Coins // Coins in account on Initialization
ReceivedCoins sdk.Coins // Coins received from other accounts
SentCoins sdk.Coins // Coins sent to other accounts
// StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point
StartTime time.Time
EndTime time.Time
}
// Uses time in context to calculate total unlocked coins
SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins:
// Coins unlocked by vesting schedule
unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)
// Must still check for currentCoins constraint since some unlocked coins may have been delegated.
currentCoins := vacc.BaseAccount.GetCoins()
// min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins
return min(unlockedCoins, currentCoins)
```
The `VestingAccount` interface is used to assert that an account is a vesting account like so:
```go
vacc, ok := acc.(VestingAccount); ok
```
as well as to calculate the SendableCoins at any given moment.
The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`,
`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point.
Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements
the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of
both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period.
##### Changes to Keepers/Handler
Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be
handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like
`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above.
```go
if acc is VestingAccount and Now < vestingAccount.EndTime:
// Check if amount is less than currently allowed sendable coins
if msg.Amount > vestingAccount.SendableCoins(ctx) then fail
else:
vestingAccount.SentCoins += msg.Amount
else:
// Account has fully vested, treat like regular account
if msg.Amount > account.GetCoins() then fail
// All checks passed, send the coins
SendCoins(inputs, outputs)
```
Coins that are sent to a vesting account after initialization by users sending them coins should be spendable
immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not
originally own should increment `ReceivedCoins` by the amount sent.
Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute.
CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`.
However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented.
### Initializing at Genesis
To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be
BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts
as appropriate.
```go
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
GenesisCoins sdk.Coins `json:"coins"`
EndTime int64 `json:"lock"`
}
initChainer:
for gacc in GenesisAccounts:
baseAccount := BaseAccount{
Address: gacc.Address,
Coins: gacc.GenesisCoins,
}
if gacc.EndTime != 0:
vestingAccount := ContinuouslyVestingAccount{
BaseAccount: baseAccount,
OriginalVestingCoins: gacc.GenesisCoins,
StartTime: RequestInitChain.Time,
EndTime: gacc.EndTime,
}
AddAccountToState(vestingAccount)
else:
AddAccountToState(baseAccount)
```
### Formulas
`OriginalVestingCoins`: Amount of coins in account at Genesis
`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`)
`ReceivedCoins`: Coins received from other accounts (always unlocked)
`LockedCoins`: Coins that are currently locked
`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked)
`Sent`: Coins sent to other accounts (MUST be unlocked)
Maximum amount of coins vesting schedule allows to be sent:
`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)`
`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins`
Coins currently in Account:
`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent`
`CurrentCoins = vestingAccount.GetCoins()`
**Maximum amount of coins spendable right now:**
`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )`

View File

@ -1,10 +1,10 @@
# Validator Setup
::: warning Current Testnet
The current testnet is `gaia-7005`.
The current testnet is `gaia-8000`.
:::
Before setting up your validator node, make sure you've already gone through the [Full Node Setup](/getting-started/full-node.md) guide.
Before setting up your validator node, make sure you've already gone through the [Full Node Setup](/docs/getting-started/full-node.md) guide.
## Running a Validator Node
@ -31,15 +31,10 @@ Don't use more `steak` thank you have! You can always get more by using the [Fau
```bash
gaiacli stake create-validator \
--amount=5steak \
<<<<<<< HEAD
--pubkey=$(gaiad tendermint show-validator) \
--address-validator=<account_cosmosaccaddr>
=======
--pubkey=$(gaiad tendermint show_validator) \
--validator=<account_cosmosaccaddr>
>>>>>>> 6f19f2ed... Rename --address-validator flag to --validator
--moniker="choose a moniker" \
--chain-id=gaia-7005 \
--chain-id=<chain_id> \
--name=<key_name>
```
@ -56,7 +51,7 @@ gaiacli stake edit-validator
--website="https://cosmos.network" \
--identity=6A0D65E29A4CBC8E
--details="To infinity and beyond!"
--chain-id=gaia-7005 \
--chain-id=<chain_id> \
--name=<key_name>
```
@ -65,14 +60,28 @@ gaiacli stake edit-validator
View the validator's information with this command:
```bash
gaiacli stake validator \
<<<<<<< HEAD
--address-validator=<account_cosmosaccaddr> \
--chain-id=gaia-7005
=======
gaiacli stake validator <account_cosmosaccaddr>
```
### Track Validator Signing Information
In order to keep track of a validator's signatures in the past you can do so by using the `signing-info` command:
```bash
gaiacli stake signing-information <validator-pubkey>\
--chain-id=<chain_id>
```
### Unrevoke Validator
When a validator is `Revoked` for downtime, you must submit an `Unrevoke` transaction in order to be able to get block proposer rewards again (depends on the zone fee distribution).
```bash
gaiacli stake unrevoke \
--from=<key_name> \
--chain-id=<chain_id>
--validator=<account_cosmosaccaddr> \
--chain-id=gaia-6002
>>>>>>> 6f19f2ed... Rename --address-validator flag to --validator
```
### Confirm Your Validator is Running
@ -80,12 +89,11 @@ gaiacli stake validator \
Your validator is active if the following command returns anything:
```bash
gaiacli advanced tendermint validator-set | grep "$(gaiad tendermint show-validator)"
gaiacli tendermint validator-set | grep "$(gaiad tendermint show-validator)"
```
You should also be able to see your validator on the [Explorer](https://explorecosmos.network/validators). You are looking for the `bech32` encoded `address` in the `~/.gaiad/config/priv_validator.json` file.
::: warning Note
To be in the validator set, you need to have more total voting power than the 100th validator.
:::
@ -94,7 +102,7 @@ To be in the validator set, you need to have more total voting power than the 10
### Problem #1: My validator has `voting_power: 0`
Your validator has become auto-unbonded. In `gaia-7005`, we unbond validators if they do not vote on `50` of the last `100` blocks. Since blocks are proposed every ~2 seconds, a validator unresponsive for ~100 seconds will become unbonded. This usually happens when your `gaiad` process crashes.
Your validator has become auto-unbonded. In `gaia-8000`, we unbond validators if they do not vote on `50` of the last `100` blocks. Since blocks are proposed every ~2 seconds, a validator unresponsive for ~100 seconds will become unbonded. This usually happens when your `gaiad` process crashes.
Here's how you can return the voting power back to your validator. First, if `gaiad` is not running, start it up again:
@ -105,7 +113,7 @@ gaiad start
Wait for your full node to catch up to the latest block. Next, run the following command. Note that `<cosmosaccaddr>` is the address of your validator account, and `<name>` is the name of the validator account. You can find this info by running `gaiacli keys list`.
```bash
gaiacli stake unrevoke <cosmosaccaddr> --chain-id=gaia-7005 --name=<name>
gaiacli stake unrevoke <cosmosaccaddr> --chain-id=<chain_id> --name=<name>
```
::: danger Warning

View File

@ -14,6 +14,7 @@ import (
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/libs/cli"
@ -51,6 +52,13 @@ func main() {
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
stakecmd.GetCmdQueryPool("stake", cdc),
stakecmd.GetCmdQueryParams("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc),
stakecmd.GetCmdQueryRedelegation("stake", cdc),
stakecmd.GetCmdQueryRedelegations("stake", cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
)...)
@ -63,6 +71,8 @@ func main() {
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond("stake", cdc),
stakecmd.GetCmdRedelegate("stake", cdc),
slashingcmd.GetCmdUnrevoke(cdc),
)...)
// add proxy, version and key info

View File

@ -61,7 +61,7 @@ rm -rf ./build
make build-linux
# Create configuration
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet --o . --v 1
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet -o . --v 1
#Run the node
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode

View File

@ -8,7 +8,7 @@ Restart=on-failure
User=gaiad
Group=gaiad
PermissionsStartOnly=true
ExecStart=/usr/bin/gaiacli advanced rest-server --laddr {{GAIAD_ADDRESS}}
ExecStart=/usr/bin/gaiacli rest-server --laddr {{GAIAD_ADDRESS}}
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGTERM

41
scripts/localnet-blocks-test.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash
CNT=0
ITER=$1
SLEEP=$2
NUMBLOCKS=$3
NODEADDR=$4
if [ -z "$1" ]; then
echo "Need to input number of iterations to run..."
exit 1
fi
if [ -z "$2" ]; then
echo "Need to input number of seconds to sleep between iterations"
exit 1
fi
if [ -z "$3" ]; then
echo "Need to input block height to declare completion..."
exit 1
fi
if [ -z "$4" ]; then
echo "Need to input node address to poll..."
exit 1
fi
while [ ${CNT} -lt $ITER ]; do
var=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height')
echo "Number of Blocks: ${var}"
if [ ! -z ${var} ] && [ ${var} -gt ${NUMBLOCKS} ]; then
echo "Number of blocks reached, exiting success..."
exit 0
fi
let CNT=CNT+1
sleep $SLEEP
done
echo "Timeout reached, exiting failure..."
exit 1

View File

@ -20,7 +20,7 @@ import (
var (
nodeDirPrefix = "node-dir-prefix"
nValidators = "v"
outputDir = "o"
outputDir = "output-dir"
startingIPAddress = "starting-ip-address"
)
@ -49,7 +49,7 @@ Example:
}
cmd.Flags().Int(nValidators, 4,
"Number of validators to initialize the testnet with")
cmd.Flags().String(outputDir, "./mytestnet",
cmd.Flags().StringP(outputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet")
cmd.Flags().String(nodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)")

View File

@ -69,7 +69,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command {
func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
return &cobra.Command{
Use: "unsafe-reset-all",
Short: "Reset blockchain database, priv_validator.json file, and the logger",
Short: "Resets the blockchain database, removes address book files, and resets priv_validator.json to the genesis state",
RunE: func(cmd *cobra.Command, args []string) error {
cfg := ctx.Config
tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger)

View File

@ -68,6 +68,9 @@ func (rs *rootMultiStore) MountStoreWithDB(key StoreKey, typ StoreType, db dbm.D
if _, ok := rs.storesParams[key]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate store key %v", key))
}
if _, ok := rs.keysByName[key.Name()]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate store key name %v", key))
}
rs.storesParams[key] = storeParams{
key: key,
typ: typ,

View File

@ -21,6 +21,21 @@ func TestStoreType(t *testing.T) {
}
func TestStoreMount(t *testing.T) {
db := dbm.NewMemDB()
store := NewCommitMultiStore(db)
key1 := sdk.NewKVStoreKey("store1")
key2 := sdk.NewKVStoreKey("store2")
dup1 := sdk.NewKVStoreKey("store1")
require.NotPanics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) })
require.NotPanics(t, func() { store.MountStoreWithDB(key2, sdk.StoreTypeIAVL, db) })
require.Panics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) })
require.Panics(t, func() { store.MountStoreWithDB(dup1, sdk.StoreTypeIAVL, db) })
}
func TestMultistoreCommitLoad(t *testing.T) {
var db dbm.DB = dbm.NewMemDB()
if useDebugDB {

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -e
PKGS=$(go list ./... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test)
PKGS=$(go list ./... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | grep -v '/simulation')
set -e
echo "mode: atomic" > coverage.txt

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
@ -108,6 +109,23 @@ func (bz AccAddress) Format(s fmt.State, verb rune) {
}
}
// Returns boolean for whether two AccAddresses are Equal
func (bz AccAddress) Equals(bz2 AccAddress) bool {
if bz.Empty() && bz2.Empty() {
return true
}
return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0
}
// Returns boolean for whether an AccAddress is empty
func (bz AccAddress) Empty() bool {
if bz == nil {
return true
}
bz2 := AccAddress{}
return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0
}
//__________________________________________________________
// AccAddress a wrapper around bytes meant to represent a validator address
@ -192,6 +210,23 @@ func (bz ValAddress) Format(s fmt.State, verb rune) {
}
}
// Returns boolean for whether two ValAddresses are Equal
func (bz ValAddress) Equals(bz2 ValAddress) bool {
if bz.Empty() && bz2.Empty() {
return true
}
return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0
}
// Returns boolean for whether an AccAddress is empty
func (bz ValAddress) Empty() bool {
if bz == nil {
return true
}
bz2 := ValAddress{}
return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0
}
// Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string
func Bech32ifyAccPub(pub crypto.PubKey) (string, error) {
return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes())

6
types/queryable.go Normal file
View File

@ -0,0 +1,6 @@
package types
import abci "github.com/tendermint/tendermint/abci/types"
// Type for querier functions on keepers to implement to handle custom queries
type Querier = func(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error)

View File

@ -51,8 +51,9 @@ type Validator interface {
// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()),
Power: v.GetPower().RoundInt64(),
PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()),
Address: v.GetPubKey().Address(),
Power: v.GetPower().RoundInt64(),
}
}

View File

@ -2,10 +2,10 @@
package version
const Maj = "0"
const Min = "23"
const Min = "24"
const Fix = "1"
const Version = "0.23.1"
const Version = "0.24.1"
// GitCommit set by build flags
var GitCommit = ""

View File

@ -5,13 +5,17 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
const (
deductFeesCost sdk.Gas = 10
memoCostPerByte sdk.Gas = 1
verifyCost = 100
maxMemoCharacters = 100
deductFeesCost sdk.Gas = 10
memoCostPerByte sdk.Gas = 1
ed25519VerifyCost = 59
secp256k1VerifyCost = 100
maxMemoCharacters = 100
)
// NewAnteHandler returns an AnteHandler that checks
@ -187,7 +191,7 @@ func processSig(
}
// Check sig.
ctx.GasMeter().ConsumeGas(verifyCost, "ante verify")
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}
@ -195,6 +199,17 @@ func processSig(
return
}
func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {
switch pubkey.(type) {
case ed25519.PubKeyEd25519:
meter.ConsumeGas(ed25519VerifyCost, "ante verify: ed25519")
case secp256k1.PubKeySecp256k1:
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: secp256k1")
default:
panic("Unrecognized signature type")
}
}
// Deduct the fee from the account.
// We could use the CoinKeeper (in addition to the AccountMapper,
// because the CoinKeeper doesn't give us accounts), but it seems easier to do this.

View File

@ -35,6 +35,10 @@ func TestAndRunSingleInputMsgSend(mapper auth.AccountMapper) simulation.TestAndR
toAddr := sdk.AccAddress(toKey.PubKey().Address())
initFromCoins := mapper.GetAccount(ctx, fromAddr).GetCoins()
if len(initFromCoins) == 0 {
return "skipping, no coins at all", nil
}
denomIndex := r.Intn(len(initFromCoins))
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
if goErr != nil {

View File

@ -5,6 +5,8 @@ import (
"math/rand"
"testing"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
@ -24,7 +26,7 @@ func TestBankWithRandomMessages(t *testing.T) {
panic(err)
}
appStateFn := func(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage {
appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
mock.RandomSetGenesis(r, mapp, accs, []string{"stake"})
return json.RawMessage("{}")
}
@ -39,6 +41,7 @@ func TestBankWithRandomMessages(t *testing.T) {
NonnegativeBalanceInvariant(mapper),
TotalCoinsInvariant(mapper, func() sdk.Coins { return mapp.TotalCoinsSupply }),
},
100, 30, 30,
30, 30,
false,
)
}

View File

@ -15,6 +15,9 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"io/ioutil"
"encoding/json"
"strings"
)
const (
@ -28,18 +31,51 @@ const (
flagDepositer = "depositer"
flagStatus = "status"
flagLatestProposalIDs = "latest"
flagProposal = "proposal"
)
type proposal struct {
Title string
Description string
Type string
Deposit string
}
var proposalFlags = []string{
flagTitle,
flagDescription,
flagProposalType,
flagDeposit,
}
// GetCmdSubmitProposal implements submitting a proposal transaction command.
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "submit-proposal",
Short: "Submit a proposal along with an initial deposit",
Long: strings.TrimSpace(`
Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example:
$ gaiacli gov submit-proposal --proposal="path/to/proposal.json"
where proposal.json contains:
{
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
}
is equivalent to
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test"
`),
RunE: func(cmd *cobra.Command, args []string) error {
title := viper.GetString(flagTitle)
description := viper.GetString(flagDescription)
strProposalType := viper.GetString(flagProposalType)
initialDeposit := viper.GetString(flagDeposit)
proposal, err := parseSubmitProposalFlags()
if err != nil {
return err
}
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
return err
}
amount, err := sdk.ParseCoins(initialDeposit)
amount, err := sdk.ParseCoins(proposal.Deposit)
if err != nil {
return err
}
proposalType, err := gov.ProposalTypeFromString(strProposalType)
proposalType, err := gov.ProposalTypeFromString(proposal.Type)
if err != nil {
return err
}
msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount)
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
err = msg.ValidateBasic()
if err != nil {
@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
cmd.Flags().String(flagDescription, "", "description of proposal")
cmd.Flags().String(flagProposalType, "", "proposalType of proposal")
cmd.Flags().String(flagDeposit, "", "deposit of proposal")
cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)")
return cmd
}
func parseSubmitProposalFlags() (*proposal, error) {
proposal := &proposal{}
proposalFile := viper.GetString(flagProposal)
if proposalFile == "" {
proposal.Title = viper.GetString(flagTitle)
proposal.Description = viper.GetString(flagDescription)
proposal.Type = viper.GetString(flagProposalType)
proposal.Deposit = viper.GetString(flagDeposit)
return proposal, nil
}
for _, flag := range proposalFlags {
if viper.GetString(flag) != "" {
return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag)
}
}
contents, err := ioutil.ReadFile(proposalFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(contents, proposal)
if err != nil {
return nil, err
}
return proposal, nil
}
// GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{

View File

@ -0,0 +1,70 @@
package cli
import (
"testing"
"github.com/spf13/viper"
"io/ioutil"
"github.com/stretchr/testify/require"
)
func TestParseSubmitProposalFlags(t *testing.T) {
okJSON, err := ioutil.TempFile("", "proposal")
require.Nil(t, err, "unexpected error")
okJSON.WriteString(`
{
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
}
`)
badJSON, err := ioutil.TempFile("", "proposal")
require.Nil(t, err, "unexpected error")
badJSON.WriteString("bad json")
// nonexistent json
viper.Set(flagProposal, "fileDoesNotExist")
_, err = parseSubmitProposalFlags()
require.Error(t, err)
// invalid json
viper.Set(flagProposal, badJSON.Name())
_, err = parseSubmitProposalFlags()
require.Error(t, err)
// ok json
viper.Set(flagProposal, okJSON.Name())
proposal1, err := parseSubmitProposalFlags()
require.Nil(t, err, "unexpected error")
require.Equal(t, "Test Proposal", proposal1.Title)
require.Equal(t, "My awesome proposal", proposal1.Description)
require.Equal(t, "Text", proposal1.Type)
require.Equal(t, "1000test", proposal1.Deposit)
// flags that can't be used with --proposal
for _, incompatibleFlag := range proposalFlags {
viper.Set(incompatibleFlag, "some value")
_, err := parseSubmitProposalFlags()
require.Error(t, err)
viper.Set(incompatibleFlag, "")
}
// no --proposal, only flags
viper.Set(flagProposal, "")
viper.Set(flagTitle, proposal1.Title)
viper.Set(flagDescription, proposal1.Description)
viper.Set(flagProposalType, proposal1.Type)
viper.Set(flagDeposit, proposal1.Deposit)
proposal2, err := parseSubmitProposalFlags()
require.Nil(t, err, "unexpected error")
require.Equal(t, proposal1.Title, proposal2.Title)
require.Equal(t, proposal1.Description, proposal2.Description)
require.Equal(t, proposal1.Type, proposal2.Type)
require.Equal(t, proposal1.Deposit, proposal2.Deposit)
err = okJSON.Close()
require.Nil(t, err, "unexpected error")
err = badJSON.Close()
require.Nil(t, err, "unexpected error")
}

View File

@ -3,7 +3,6 @@ package rest
import (
"fmt"
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -17,10 +16,11 @@ import (
// REST Variable names
// nolint
const (
RestProposalID = "proposalID"
RestProposalID = "proposal-id"
RestDepositer = "depositer"
RestVoter = "voter"
RestProposalStatus = "status"
RestNumLatest = "latest"
storeName = "gov"
)
@ -97,16 +97,13 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
err := errors.Errorf("proposalID [%d] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
var req depositReq
err = buildReq(w, r, cdc, &req)
err := buildReq(w, r, cdc, &req)
if err != nil {
return
}
@ -139,15 +136,13 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
err := errors.Errorf("proposalID [%d] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
var req voteReq
err = buildReq(w, r, cdc, &req)
err := buildReq(w, r, cdc, &req)
if err != nil {
return
}
@ -180,36 +175,33 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
err := errors.Errorf("proposalID [%d] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
if err != nil || len(res) == 0 {
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
return
params := gov.QueryProposalParams{
ProposalID: proposalID,
}
var proposal gov.Proposal
cdc.MustUnmarshalBinary(res, &proposal)
output, err := wire.MarshalJSONIndent(cdc, proposal)
bz, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
w.Write(res)
}
}
@ -227,12 +219,8 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("proposalID [%d] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
@ -255,36 +243,43 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName)
if err != nil || len(res) == 0 {
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
if err != nil || len(res) == 0 {
w.WriteHeader(http.StatusNotFound)
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
params := gov.QueryDepositParams{
ProposalID: proposalID,
Depositer: depositerAddr,
}
return
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusNotFound)
err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID)
res, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
var deposit gov.Deposit
cdc.MustUnmarshalBinary(res, &deposit)
output, err := wire.MarshalJSONIndent(cdc, deposit)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
cdc.UnmarshalJSON(res, &deposit)
if deposit.Empty() {
res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID}))
if err != nil || len(res) == 0 {
w.WriteHeader(http.StatusNotFound)
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusNotFound)
err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
w.Write(res)
}
}
@ -301,12 +296,8 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("proposalID [%s] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
@ -328,36 +319,47 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
if err != nil || len(res) == 0 {
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
if err != nil || len(res) == 0 {
w.WriteHeader(http.StatusNotFound)
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
params := gov.QueryVoteParams{
Voter: voterAddr,
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
return
}
w.WriteHeader(http.StatusNotFound)
err = errors.Errorf("voter [%s] did not vote on proposalID [%d]", bechVoterAddr, proposalID)
res, err := cliCtx.QueryWithData("custom/gov/vote", bz)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
var vote gov.Vote
cdc.MustUnmarshalBinary(res, &vote)
output, err := wire.MarshalJSONIndent(cdc, vote)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
cdc.UnmarshalJSON(res, &vote)
if vote.Empty() {
bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID})
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
w.WriteHeader(http.StatusNotFound)
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusNotFound)
err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
w.Write(res)
}
}
@ -376,59 +378,31 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
return
}
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("proposalID [%s] is not positive", proposalID)
w.Write([]byte(err.Error()))
proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w)
if !ok {
return
}
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
if err != nil || len(res) == 0 {
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
w.Write([]byte(err.Error()))
return
params := gov.QueryVotesParams{
ProposalID: proposalID,
}
var proposal gov.Proposal
cdc.MustUnmarshalBinary(res, &proposal)
if proposal.GetStatus() != gov.StatusVotingPeriod {
err := errors.Errorf("proposal is not in Voting Period", proposalID)
w.Write([]byte(err.Error()))
return
}
res2, err := cliCtx.QuerySubspace(gov.KeyVotesSubspace(proposalID), storeName)
if err != nil {
err = errors.New("ProposalID doesn't exist")
w.Write([]byte(err.Error()))
return
}
var votes []gov.Vote
for i := 0; i < len(res2); i++ {
var vote gov.Vote
cdc.MustUnmarshalBinary(res2[i].Value, &vote)
votes = append(votes, vote)
}
output, err := wire.MarshalJSONIndent(cdc, votes)
bz, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
res, err := cliCtx.QueryWithData("custom/gov/votes", bz)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(res)
}
}
@ -439,24 +413,23 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
bechVoterAddr := r.URL.Query().Get(RestVoter)
bechDepositerAddr := r.URL.Query().Get(RestDepositer)
strProposalStatus := r.URL.Query().Get(RestProposalStatus)
strNumLatest := r.URL.Query().Get(RestNumLatest)
var err error
var voterAddr sdk.AccAddress
var depositerAddr sdk.AccAddress
var proposalStatus gov.ProposalStatus
params := gov.QueryProposalsParams{}
if len(bechVoterAddr) != 0 {
voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr)
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter)
w.Write([]byte(err.Error()))
return
}
params.Voter = voterAddr
}
if len(bechDepositerAddr) != 0 {
depositerAddr, err = sdk.AccAddressFromBech32(bechDepositerAddr)
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer)
@ -464,10 +437,11 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
return
}
params.Depositer = depositerAddr
}
if len(strProposalStatus) != 0 {
proposalStatus, err = gov.ProposalStatusFromString(strProposalStatus)
proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus)
@ -475,63 +449,33 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
return
}
params.ProposalStatus = proposalStatus
}
if len(strNumLatest) != 0 {
numLatest, ok := parseInt64OrReturnBadRequest(strNumLatest, w)
if !ok {
return
}
params.NumLatestProposals = numLatest
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(gov.KeyNextProposalID, storeName)
res, err := cliCtx.QueryWithData("custom/gov/proposals", bz)
if err != nil {
err = errors.New("no proposals exist yet and proposalID has not been set")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
var maxProposalID int64
cdc.MustUnmarshalBinary(res, &maxProposalID)
matchingProposals := []gov.Proposal{}
for proposalID := int64(0); proposalID < maxProposalID; proposalID++ {
if voterAddr != nil {
res, err = cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
if err != nil || len(res) == 0 {
continue
}
}
if depositerAddr != nil {
res, err = cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName)
if err != nil || len(res) == 0 {
continue
}
}
res, err = cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
if err != nil || len(res) == 0 {
continue
}
var proposal gov.Proposal
cdc.MustUnmarshalBinary(res, &proposal)
if len(strProposalStatus) != 0 {
if proposal.GetStatus() != proposalStatus {
continue
}
}
matchingProposals = append(matchingProposals, proposal)
}
output, err := wire.MarshalJSONIndent(cdc, matchingProposals)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
w.Write(res)
}
}

View File

@ -1,8 +1,10 @@
package rest
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -100,3 +102,15 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base
w.Write(output)
}
func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok bool) {
var err error
n, err = strconv.ParseInt(s, 10, 64)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err := fmt.Errorf("'%s' is not a valid int64", s)
w.Write([]byte(err.Error()))
return 0, false
}
return n, true
}

View File

@ -15,6 +15,17 @@ type Vote struct {
Option VoteOption `json:"option"` // option from OptionSet chosen by the voter
}
// Returns whether 2 votes are equal
func (voteA Vote) Equals(voteB Vote) bool {
return voteA.Voter.Equals(voteB.Voter) && voteA.ProposalID == voteB.ProposalID && voteA.Option == voteB.Option
}
// Returns whether a vote is empty
func (voteA Vote) Empty() bool {
voteB := Vote{}
return voteA.Equals(voteB)
}
// Deposit
type Deposit struct {
Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer
@ -22,6 +33,17 @@ type Deposit struct {
Amount sdk.Coins `json:"amount"` // Deposit amount
}
// Returns whether 2 deposits are equal
func (depositA Deposit) Equals(depositB Deposit) bool {
return depositA.Depositer.Equals(depositB.Depositer) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount)
}
// Returns whether a deposit is empty
func (depositA Deposit) Empty() bool {
depositB := Deposit{}
return depositA.Equals(depositB)
}
// Type that represents VoteOption as a byte
type VoteOption byte

View File

@ -1,6 +1,8 @@
package gov
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/tags"
)
@ -96,6 +98,8 @@ func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result {
// Called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
logger := ctx.Logger().With("module", "x/gov")
resTags = sdk.NewTags()
// Delete proposals that haven't met minDeposit
@ -109,6 +113,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
keeper.DeleteProposal(ctx, inactiveProposal)
resTags.AppendTag(tags.Action, tags.ActionProposalDropped)
resTags.AppendTag(tags.ProposalID, proposalIDBytes)
logger.Info("Proposal %d - \"%s\" - didn't mean minimum deposit (had only %s), deleted",
inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), inactiveProposal.GetTotalDeposit())
}
// Check if earliest Active Proposal ended voting period yet
@ -136,6 +143,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
activeProposal.SetTallyResult(tallyResults)
keeper.SetProposal(ctx, activeProposal)
logger.Info("Proposal %d - \"%s\" - tallied, passed: %v",
activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)
for _, valAddr := range nonVotingVals {
val := keeper.ds.GetValidatorSet().Validator(ctx, valAddr)
keeper.ds.GetValidatorSet().Slash(ctx,
@ -143,6 +153,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
ctx.BlockHeight(),
val.GetPower().RoundInt64(),
keeper.GetTallyingProcedure(ctx).GovernancePenalty)
logger.Info(fmt.Sprintf("Validator %s failed to vote on proposal %d, slashing",
val.GetOperator(), activeProposal.GetProposalID()))
}
resTags.AppendTag(tags.Action, action)

View File

@ -108,6 +108,52 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) {
store.Delete(KeyProposal(proposal.GetProposalID()))
}
// nolint: gocyclo
// Get Proposal from store by ProposalID
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal {
maxProposalID, err := keeper.peekCurrentProposalID(ctx)
if err != nil {
return nil
}
matchingProposals := []Proposal{}
if numLatest <= 0 {
numLatest = maxProposalID
}
for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ {
if voterAddr != nil && len(voterAddr) != 0 {
_, found := keeper.GetVote(ctx, proposalID, voterAddr)
if !found {
continue
}
}
if depositerAddr != nil && len(depositerAddr) != 0 {
_, found := keeper.GetDeposit(ctx, proposalID, depositerAddr)
if !found {
continue
}
}
proposal := keeper.GetProposal(ctx, proposalID)
if proposal == nil {
continue
}
if validProposalStatus(status) {
if proposal.GetStatus() != status {
continue
}
}
matchingProposals = append(matchingProposals, proposal)
}
return matchingProposals
}
func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk.Error {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(KeyNextProposalID)
@ -119,6 +165,17 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk
return nil
}
// Get the last used proposal ID
func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) {
proposalID, err := keeper.peekCurrentProposalID(ctx)
if err != nil {
return 0
}
proposalID--
return
}
// Gets the next available ProposalID and increments it
func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(KeyNextProposalID)
@ -131,6 +188,17 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd
return proposalID, nil
}
// Peeks the next available ProposalID without incrementing it
func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(KeyNextProposalID)
if bz == nil {
return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set")
}
keeper.cdc.MustUnmarshalBinary(bz, &proposalID)
return proposalID, nil
}
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) {
proposal.SetVotingStartBlock(ctx.BlockHeight())
proposal.SetStatus(StatusVotingPeriod)

View File

@ -110,6 +110,7 @@ type ProposalKind byte
//nolint
const (
ProposalTypeNil ProposalKind = 0x00
ProposalTypeText ProposalKind = 0x01
ProposalTypeParameterChange ProposalKind = 0x02
ProposalTypeSoftwareUpgrade ProposalKind = 0x03
@ -203,6 +204,7 @@ type ProposalStatus byte
//nolint
const (
StatusNil ProposalStatus = 0x00
StatusDepositPeriod ProposalStatus = 0x01
StatusVotingPeriod ProposalStatus = 0x02
StatusPassed ProposalStatus = 0x03
@ -220,6 +222,8 @@ func ProposalStatusFromString(str string) (ProposalStatus, error) {
return StatusPassed, nil
case "Rejected":
return StatusRejected, nil
case "":
return StatusNil, nil
default:
return ProposalStatus(0xff), errors.Errorf("'%s' is not a valid proposal status", str)
}

213
x/gov/queryable.go Normal file
View File

@ -0,0 +1,213 @@
package gov
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
abci "github.com/tendermint/tendermint/abci/types"
)
func NewQuerier(keeper Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
switch path[0] {
case "proposal":
return queryProposal(ctx, path[1:], req, keeper)
case "deposit":
return queryDeposit(ctx, path[1:], req, keeper)
case "vote":
return queryVote(ctx, path[1:], req, keeper)
case "deposits":
return queryDeposits(ctx, path[1:], req, keeper)
case "votes":
return queryVotes(ctx, path[1:], req, keeper)
case "proposals":
return queryProposals(ctx, path[1:], req, keeper)
case "tally":
return queryTally(ctx, path[1:], req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown gov query endpoint")
}
}
}
// Params for query 'custom/gov/proposal'
type QueryProposalParams struct {
ProposalID int64
}
func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryProposalParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
proposal := keeper.GetProposal(ctx, params.ProposalID)
if proposal == nil {
return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID)
}
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposal)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/deposit'
type QueryDepositParams struct {
ProposalID int64
Depositer sdk.AccAddress
}
func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryDepositParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer)
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposit)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/vote'
type QueryVoteParams struct {
ProposalID int64
Voter sdk.AccAddress
}
func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryVoteParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter)
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, vote)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/deposits'
type QueryDepositsParams struct {
ProposalID int64
}
func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryDepositParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
var deposits []Deposit
depositsIterator := keeper.GetDeposits(ctx, params.ProposalID)
for ; depositsIterator.Valid(); depositsIterator.Next() {
deposit := Deposit{}
keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit)
deposits = append(deposits, deposit)
}
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposits)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/votes'
type QueryVotesParams struct {
ProposalID int64
}
func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryVotesParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
var votes []Vote
votesIterator := keeper.GetVotes(ctx, params.ProposalID)
for ; votesIterator.Valid(); votesIterator.Next() {
vote := Vote{}
keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote)
votes = append(votes, vote)
}
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, votes)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/proposals'
type QueryProposalsParams struct {
Voter sdk.AccAddress
Depositer sdk.AccAddress
ProposalStatus ProposalStatus
NumLatestProposals int64
}
func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var params QueryProposalsParams
err2 := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals)
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposals)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}
// Params for query 'custom/gov/tally'
type QueryTallyParams struct {
ProposalID int64
}
func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
// TODO: Dependant on #1914
var proposalID int64
err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
proposal := keeper.GetProposal(ctx, proposalID)
if proposal == nil {
return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID)
}
var tallyResult TallyResult
if proposal.GetStatus() == StatusDepositPeriod {
tallyResult = EmptyTallyResult()
} else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected {
tallyResult = proposal.GetTallyResult()
} else {
_, tallyResult, _ = tally(ctx, keeper, proposal)
}
bz, err2 := wire.MarshalJSONIndent(keeper.cdc, tallyResult)
if err2 != nil {
panic("could not marshal result to JSON")
}
return bz, nil
}

View File

@ -0,0 +1,19 @@
package simulation
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
)
// AllInvariants tests all governance invariants
func AllInvariants() simulation.Invariant {
return func(t *testing.T, app *baseapp.BaseApp, log string) {
// TODO Add some invariants!
// Checking proposal queues, no passed-but-unexecuted proposals, etc.
require.Nil(t, nil)
}
}

132
x/gov/simulation/msgs.go Normal file
View File

@ -0,0 +1,132 @@
package simulation
import (
"fmt"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/stake"
)
const (
denom = "steak"
)
// SimulateMsgSubmitProposal
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.TestAndRunTx {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
key := simulation.RandomKey(r, keys)
addr := sdk.AccAddress(key.PubKey().Address())
deposit := randomDeposit(r)
msg := gov.NewMsgSubmitProposal(
simulation.RandStringOfLength(r, 5),
simulation.RandStringOfLength(r, 5),
gov.ProposalTypeText,
addr,
deposit,
)
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
ctx, write := ctx.CacheContext()
result := gov.NewHandler(k)(ctx, msg)
if result.IsOK() {
// Update pool to keep invariants
pool := sk.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(deposit.AmountOf(denom)))
sk.SetPool(ctx, pool)
write()
}
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil
}
}
// SimulateMsgDeposit
func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.TestAndRunTx {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
key := simulation.RandomKey(r, keys)
addr := sdk.AccAddress(key.PubKey().Address())
proposalID, ok := randomProposalID(r, k, ctx)
if !ok {
return "no-operation", nil
}
deposit := randomDeposit(r)
msg := gov.NewMsgDeposit(addr, proposalID, deposit)
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
ctx, write := ctx.CacheContext()
result := gov.NewHandler(k)(ctx, msg)
if result.IsOK() {
// Update pool to keep invariants
pool := sk.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(deposit.AmountOf(denom)))
sk.SetPool(ctx, pool)
write()
}
event(fmt.Sprintf("gov/MsgDeposit/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgDeposit: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil
}
}
// SimulateMsgVote
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.TestAndRunTx {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
key := simulation.RandomKey(r, keys)
addr := sdk.AccAddress(key.PubKey().Address())
proposalID, ok := randomProposalID(r, k, ctx)
if !ok {
return "no-operation", nil
}
option := randomVotingOption(r)
msg := gov.NewMsgVote(addr, proposalID, option)
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
ctx, write := ctx.CacheContext()
result := gov.NewHandler(k)(ctx, msg)
if result.IsOK() {
write()
}
event(fmt.Sprintf("gov/MsgVote/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgVote: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil
}
}
// Pick a random deposit
func randomDeposit(r *rand.Rand) sdk.Coins {
// TODO Choose based on account balance and min deposit
amount := int64(r.Intn(20)) + 1
return sdk.Coins{sdk.NewInt64Coin(denom, amount)}
}
// Pick a random proposal ID
func randomProposalID(r *rand.Rand, k gov.Keeper, ctx sdk.Context) (proposalID int64, ok bool) {
lastProposalID := k.GetLastProposalID(ctx)
if lastProposalID < 1 {
return 0, false
}
proposalID = int64(r.Intn(int(lastProposalID)))
return proposalID, true
}
// Pick a random voting option
func randomVotingOption(r *rand.Rand) gov.VoteOption {
switch r.Intn(4) {
case 0:
return gov.OptionYes
case 1:
return gov.OptionAbstain
case 2:
return gov.OptionNo
case 3:
return gov.OptionNoWithVeto
}
panic("should not happen")
}

View File

@ -0,0 +1,69 @@
package simulation
import (
"encoding/json"
"math/rand"
"testing"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/stake"
)
// TestGovWithRandomMessages
func TestGovWithRandomMessages(t *testing.T) {
mapp := mock.NewApp()
bank.RegisterWire(mapp.Cdc)
gov.RegisterWire(mapp.Cdc)
mapper := mapp.AccountMapper
coinKeeper := bank.NewKeeper(mapper)
stakeKey := sdk.NewKVStoreKey("stake")
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, coinKeeper, stake.DefaultCodespace)
paramKey := sdk.NewKVStoreKey("params")
paramKeeper := params.NewKeeper(mapp.Cdc, paramKey)
govKey := sdk.NewKVStoreKey("gov")
govKeeper := gov.NewKeeper(mapp.Cdc, govKey, paramKeeper.Setter(), coinKeeper, stakeKeeper, gov.DefaultCodespace)
mapp.Router().AddRoute("gov", gov.NewHandler(govKeeper))
mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
gov.EndBlocker(ctx, govKeeper)
return abci.ResponseEndBlock{}
})
err := mapp.CompleteSetup([]*sdk.KVStoreKey{stakeKey, paramKey, govKey})
if err != nil {
panic(err)
}
appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
mock.RandomSetGenesis(r, mapp, accs, []string{"stake"})
return json.RawMessage("{}")
}
setup := func(r *rand.Rand, privKeys []crypto.PrivKey) {
ctx := mapp.NewContext(false, abci.Header{})
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())
}
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.TestAndRunTx{
SimulateMsgSubmitProposal(govKeeper, stakeKeeper),
SimulateMsgDeposit(govKeeper, stakeKeeper),
SimulateMsgVote(govKeeper, stakeKeeper),
}, []simulation.RandSetup{
setup,
}, []simulation.Invariant{
AllInvariants(),
}, 10, 100,
false,
)
}

View File

@ -11,6 +11,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
)
@ -173,7 +174,32 @@ func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccA
keys = make([]crypto.PrivKey, n, n)
addrs = make([]sdk.AccAddress, n, n)
for i := 0; i < n; i++ {
keys[i] = ed25519.GenPrivKey()
if rand.Int63()%2 == 0 {
keys[i] = secp256k1.GenPrivKey()
} else {
keys[i] = ed25519.GenPrivKey()
}
addrs[i] = sdk.AccAddress(keys[i].PubKey().Address())
}
return
}
// GeneratePrivKeyAddressPairsFromRand generates a total of n private key, address
// pairs using the provided randomness source.
func GeneratePrivKeyAddressPairsFromRand(rand *rand.Rand, n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) {
keys = make([]crypto.PrivKey, n, n)
addrs = make([]sdk.AccAddress, n, n)
for i := 0; i < n; i++ {
secret := make([]byte, 32)
_, err := rand.Read(secret)
if err != nil {
panic("Could not read randomness")
}
if rand.Int63()%2 == 0 {
keys[i] = secp256k1.GenPrivKeySecp256k1(secret)
} else {
keys[i] = ed25519.GenPrivKeyFromSecret(secret)
}
addrs[i] = sdk.AccAddress(keys[i].PubKey().Address())
}
return

View File

@ -0,0 +1,31 @@
package simulation
const (
// Fraction of double-signing evidence from a past height
pastEvidenceFraction float64 = 0.5
// Minimum time per block
minTimePerBlock int64 = 86400 / 2
// Maximum time per block
maxTimePerBlock int64 = 86400
// Number of keys
numKeys int = 250
// Chance that double-signing evidence is found on a given block
evidenceFraction float64 = 0.01
// TODO Remove in favor of binary search for invariant violation
onOperation bool = false
)
var (
// Currently there are 3 different liveness types, fully online, spotty connection, offline.
initialLivenessWeightings = []int{40, 5, 5}
livenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{
{90, 20, 1},
{10, 50, 5},
{0, 10, 1000},
})
)

View File

@ -4,78 +4,232 @@ import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"testing"
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
)
// Simulate tests application by sending random messages.
func Simulate(
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numKeys int, numBlocks int, blockSize int,
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
time := time.Now().UnixNano()
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numKeys, numBlocks, blockSize)
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit)
}
// SimulateFromSeed tests an application by running the provided
// operations, testing the provided invariants, but using the provided seed.
func SimulateFromSeed(
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numKeys int, numBlocks int, blockSize int,
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
log := fmt.Sprintf("Starting SimulateFromSeed with randomness created with seed %d", int(seed))
keys, addrs := mock.GeneratePrivKeyAddressPairs(numKeys)
fmt.Printf("%s\n", log)
r := rand.New(rand.NewSource(seed))
keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys)
// Setup event stats
events := make(map[string]uint)
event := func(what string) {
log += "\nevent - " + what
events[what]++
}
app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, addrs)})
timestamp := time.Unix(0, 0)
timeDiff := maxTimePerBlock - minTimePerBlock
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, keys, accs)})
validators := make(map[string]mockValidator)
for _, validator := range res.Validators {
validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
}
for i := 0; i < len(setups); i++ {
setups[i](r, keys)
}
app.Commit()
header := abci.Header{Height: 0}
header := abci.Header{Height: 0, Time: timestamp}
opCount := 0
request := abci.RequestBeginBlock{Header: header}
var pastTimes []time.Time
for i := 0; i < numBlocks; i++ {
app.BeginBlock(abci.RequestBeginBlock{})
// Make sure invariants hold at beginning of block and when nothing was
// done.
// Log the header time for future lookup
pastTimes = append(pastTimes, header.Time)
// Run the BeginBlock handler
app.BeginBlock(request)
log += "\nBeginBlock"
// Make sure invariants hold at beginning of block
AssertAllInvariants(t, app, invariants, log)
ctx := app.NewContext(false, header)
// TODO: Add modes to simulate "no load", "medium load", and
// "high load" blocks.
for j := 0; j < blockSize; j++ {
var thisBlockSize int
load := r.Float64()
switch {
case load < 0.33:
thisBlockSize = 0
case load < 0.66:
thisBlockSize = r.Intn(blockSize * 2)
default:
thisBlockSize = r.Intn(blockSize * 4)
}
for j := 0; j < thisBlockSize; j++ {
logUpdate, err := ops[r.Intn(len(ops))](t, r, app, ctx, keys, log, event)
log += "\n" + logUpdate
require.Nil(t, err, log)
AssertAllInvariants(t, app, invariants, log)
if onOperation {
AssertAllInvariants(t, app, invariants, log)
}
if opCount%200 == 0 {
fmt.Printf("\rSimulating... block %d/%d, operation %d.", header.Height, numBlocks, opCount)
}
opCount++
}
app.EndBlock(abci.RequestEndBlock{})
res := app.EndBlock(abci.RequestEndBlock{})
header.Height++
header.Time = header.Time.Add(time.Duration(minTimePerBlock) * time.Second).Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second)
log += "\nEndBlock"
// Make sure invariants hold at end of block
AssertAllInvariants(t, app, invariants, log)
if commit {
app.Commit()
}
// Generate a random RequestBeginBlock with the current validator set for the next block
request = RandomRequestBeginBlock(t, r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, event, header, log)
// Update the validator set
validators = updateValidators(t, r, validators, res.ValidatorUpdates, event)
}
fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds): %v\n", header.Height, header.Time)
DisplayEvents(events)
}
func getKeys(validators map[string]mockValidator) []string {
keys := make([]string, len(validators))
i := 0
for key := range validators {
keys[i] = key
i++
}
sort.Strings(keys)
return keys
}
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
func RandomRequestBeginBlock(t *testing.T, r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
pastTimes []time.Time, event func(string), header abci.Header, log string) abci.RequestBeginBlock {
if len(validators) == 0 {
return abci.RequestBeginBlock{Header: header}
}
signingValidators := make([]abci.SigningValidator, len(validators))
i := 0
for _, key := range getKeys(validators) {
mVal := validators[key]
mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState)
signed := true
if mVal.livenessState == 1 {
// spotty connection, 50% probability of success
// See https://github.com/golang/go/issues/23804#issuecomment-365370418
// for reasoning behind computing like this
signed = r.Int63()%2 == 0
} else if mVal.livenessState == 2 {
// offline
signed = false
}
if signed {
event("beginblock/signing/signed")
} else {
event("beginblock/signing/missed")
}
signingValidators[i] = abci.SigningValidator{
Validator: mVal.val,
SignedLastBlock: signed,
}
i++
}
evidence := make([]abci.Evidence, 0)
for r.Float64() < evidenceFraction {
height := header.Height
time := header.Time
if r.Float64() < pastEvidenceFraction {
height = int64(r.Intn(int(header.Height)))
time = pastTimes[height]
}
validator := signingValidators[r.Intn(len(signingValidators))].Validator
var currentTotalVotingPower int64
for _, mVal := range validators {
currentTotalVotingPower += mVal.val.Power
}
evidence = append(evidence, abci.Evidence{
Type: tmtypes.ABCIEvidenceTypeDuplicateVote,
Validator: validator,
Height: height,
Time: time,
TotalVotingPower: currentTotalVotingPower,
})
event("beginblock/evidence")
}
return abci.RequestBeginBlock{
Header: header,
LastCommitInfo: abci.LastCommitInfo{
Validators: signingValidators,
},
ByzantineValidators: evidence,
}
}
// AssertAllInvariants asserts a list of provided invariants against application state
func AssertAllInvariants(t *testing.T, app *baseapp.BaseApp, tests []Invariant, log string) {
for i := 0; i < len(tests); i++ {
tests[i](t, app, log)
}
}
// updateValidators mimicks Tendermint's update logic
func updateValidators(t *testing.T, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator {
for _, update := range updates {
switch {
case update.Power == 0:
require.NotNil(t, current[string(update.PubKey.Data)], "tried to delete a nonexistent validator")
event("endblock/validatorupdates/kicked")
delete(current, string(update.PubKey.Data))
default:
// Does validator already exist?
if mVal, ok := current[string(update.PubKey.Data)]; ok {
mVal.val = update
event("endblock/validatorupdates/updated")
} else {
// Set this new validator
current[string(update.PubKey.Data)] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
event("endblock/validatorupdates/added")
}
}
}
return current
}

View File

@ -0,0 +1,70 @@
package simulation
import (
"fmt"
"math/rand"
)
// TransitionMatrix is _almost_ a left stochastic matrix.
// It is technically not one due to not normalizing the column values.
// In the future, if we want to find the steady state distribution,
// it will be quite easy to normalize these values to get a stochastic matrix.
// Floats aren't currently used as the default due to non-determinism across
// architectures
type TransitionMatrix struct {
weights [][]int
// total in each column
totals []int
n int
}
// CreateTransitionMatrix creates a transition matrix from the provided weights.
// TODO: Provide example usage
func CreateTransitionMatrix(weights [][]int) (TransitionMatrix, error) {
n := len(weights)
for i := 0; i < n; i++ {
if len(weights[i]) != n {
return TransitionMatrix{}, fmt.Errorf("Transition Matrix: Non-square matrix provided, error on row %d", i)
}
}
totals := make([]int, n)
for row := 0; row < n; row++ {
for col := 0; col < n; col++ {
totals[col] += weights[row][col]
}
}
return TransitionMatrix{weights, totals, n}, nil
}
// NextState returns the next state randomly chosen using r, and the weightings provided
// in the transition matrix.
func (t TransitionMatrix) NextState(r *rand.Rand, i int) int {
randNum := r.Intn(t.totals[i])
for row := 0; row < t.n; row++ {
if randNum < t.weights[row][i] {
return row
}
randNum -= t.weights[row][i]
}
// This line should never get executed
return -1
}
// GetMemberOfInitialState takes an initial array of weights, of size n.
// It returns a weighted random number in [0,n).
func GetMemberOfInitialState(r *rand.Rand, weights []int) int {
n := len(weights)
total := 0
for i := 0; i < n; i++ {
total += weights[i]
}
randNum := r.Intn(total)
for state := 0; state < n; state++ {
if randNum < weights[state] {
return state
}
randNum -= weights[state]
}
// This line should never get executed
return -1
}

View File

@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
@ -25,6 +26,11 @@ type (
// If the invariant has been broken, the function should halt the
// test and output the log.
Invariant func(t *testing.T, app *baseapp.BaseApp, log string)
mockValidator struct {
val abci.Validator
livenessState int
}
)
// PeriodicInvariant returns an Invariant function closure that asserts

View File

@ -3,8 +3,9 @@ package simulation
import (
"fmt"
"math/rand"
"sort"
crypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -39,8 +40,15 @@ func RandStringOfLength(r *rand.Rand, n int) string {
// Pretty-print events as a table
func DisplayEvents(events map[string]uint) {
// TODO
fmt.Printf("Events: %v\n", events)
var keys []string
for key := range events {
keys = append(keys, key)
}
sort.Strings(keys)
fmt.Printf("Event statistics: \n")
for _, key := range keys {
fmt.Printf(" % 60s => %d\n", key, events[key])
}
}
// Pick a random key from an array

View File

@ -36,11 +36,15 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params pa
}
// handle a validator signing two blocks at the same height
func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, timestamp time.Time, power int64) {
func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractionHeight int64, timestamp time.Time, power int64) {
logger := ctx.Logger().With("module", "x/slashing")
time := ctx.BlockHeader().Time
age := time.Sub(timestamp)
address := sdk.ValAddress(pubkey.Address())
address := sdk.ValAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
if err != nil {
panic(fmt.Sprintf("Validator address %v not found", addr))
}
// Double sign too old
maxEvidenceAge := k.MaxEvidenceAge(ctx)

View File

@ -37,7 +37,7 @@ func TestHandleDoubleSign(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
// double sign less than max age
keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt)
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
// should be revoked
require.True(t, sk.Validator(ctx, addr).GetRevoked())
@ -48,7 +48,7 @@ func TestHandleDoubleSign(t *testing.T) {
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))})
// double sign past max age
keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt)
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower())
}

View File

@ -66,8 +66,8 @@ var (
// TODO Temporarily set to five minutes for testnets
defaultDoubleSignUnbondDuration int64 = 60 * 5
// TODO Temporarily set to 100 blocks for testnets
defaultSignedBlocksWindow int64 = 100
// TODO Temporarily set to 10000 blocks for testnets
defaultSignedBlocksWindow int64 = 10000
// TODO Temporarily set to 10 minutes for testnets
defaultDowntimeUnbondDuration int64 = 60 * 10

View File

@ -0,0 +1,18 @@
package simulation
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
)
// AllInvariants tests all slashing invariants
func AllInvariants() simulation.Invariant {
return func(t *testing.T, app *baseapp.BaseApp, log string) {
// TODO Any invariants to check here?
require.Nil(t, nil)
}
}

View File

@ -0,0 +1,34 @@
package simulation
import (
"fmt"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/slashing"
)
// SimulateMsgUnrevoke
func SimulateMsgUnrevoke(k slashing.Keeper) simulation.TestAndRunTx {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
key := simulation.RandomKey(r, keys)
address := sdk.AccAddress(key.PubKey().Address())
msg := slashing.NewMsgUnrevoke(address)
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
ctx, write := ctx.CacheContext()
result := slashing.NewHandler(k)(ctx, msg)
if result.IsOK() {
write()
}
event(fmt.Sprintf("slashing/MsgUnrevoke/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgUnrevoke: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil
}
}

View File

@ -29,13 +29,9 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags
// Slash any validators (and since-unbonded stake within the unbonding period)
// who contributed to valid infractions
for _, evidence := range req.ByzantineValidators {
pk, err := tmtypes.PB2TM.PubKey(evidence.Validator.PubKey)
if err != nil {
panic(err)
}
switch evidence.Type {
case tmtypes.ABCIEvidenceTypeDuplicateVote:
sk.handleDoubleSign(ctx, pk, evidence.Height, evidence.Time, evidence.Validator.Power)
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))
}

View File

@ -315,11 +315,11 @@ func GetCmdQueryUnbondingDelegations(storeName string, cdc *wire.Codec) *cobra.C
}
// GetCmdQueryRedelegation implements the command to query a single
// unbonding-delegation record.
// redelegation record.
func GetCmdQueryRedelegation(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegation",
Short: "Query an unbonding-delegation record based on delegator and validator address",
Use: "redelegation",
Short: "Query a redelegation record based on delegator and a source and destination validator address",
RunE: func(cmd *cobra.Command, args []string) error {
valSrcAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressValidatorSrc))
if err != nil {
@ -376,11 +376,11 @@ func GetCmdQueryRedelegation(storeName string, cdc *wire.Codec) *cobra.Command {
}
// GetCmdQueryRedelegations implements the command to query all the
// unbonding-delegation records for a delegator.
// redelegation records for a delegator.
func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegations [delegator-addr]",
Short: "Query all unbonding-delegations records for one delegator",
Use: "redelegations [delegator-addr]",
Short: "Query all redelegations records for one delegator",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.AccAddressFromBech32(args[0])
@ -417,3 +417,81 @@ func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command
return cmd
}
// GetCmdQueryPool implements the pool query command.
func GetCmdQueryPool(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "pool",
Short: "Query the current staking pool values",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
key := stake.PoolKey
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(key, storeName)
if err != nil {
return err
}
pool := types.MustUnmarshalPool(cdc, res)
switch viper.Get(cli.OutputFlag) {
case "text":
human := pool.HumanReadableString()
fmt.Println(human)
case "json":
// parse out the pool
output, err := wire.MarshalJSONIndent(cdc, pool)
if err != nil {
return err
}
fmt.Println(string(output))
}
return nil
},
}
return cmd
}
// GetCmdQueryPool implements the params query command.
func GetCmdQueryParams(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "parameters",
Short: "Query the current staking parameters information",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
key := stake.ParamKey
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryStore(key, storeName)
if err != nil {
return err
}
params := types.MustUnmarshalParams(cdc, res)
switch viper.Get(cli.OutputFlag) {
case "text":
human := params.HumanReadableString()
fmt.Println(human)
case "json":
// parse out the params
output, err := wire.MarshalJSONIndent(cdc, params)
if err != nil {
return err
}
fmt.Println(string(output))
}
return nil
},
}
return cmd
}

View File

@ -68,6 +68,18 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Cod
validatorHandlerFn(cliCtx, cdc),
).Methods("GET")
// Get the current state of the staking pool
r.HandleFunc(
"/stake/pool",
poolHandlerFn(cliCtx, cdc),
).Methods("GET")
// Get the current staking parameter values
r.HandleFunc(
"/stake/parameters",
paramsHandlerFn(cliCtx, cdc),
).Methods("GET")
}
// already resolve the rational shares to not handle this in the client
@ -554,3 +566,63 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handler
w.Write(output)
}
}
// HTTP request handler to query the pool information
func poolHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
key := stake.PoolKey
res, err := cliCtx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query pool. Error: %s", err.Error())))
return
}
pool, err := types.UnmarshalPool(cdc, res)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
output, err := cdc.MarshalJSON(pool)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}
// HTTP request handler to query the staking params values
func paramsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
key := stake.ParamKey
res, err := cliCtx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query parameters. Error: %s", err.Error())))
return
}
params, err := types.UnmarshalParams(cdc, res)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
output, err := cdc.MarshalJSON(params)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -33,7 +33,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
// Manually set indexes for the first time
keeper.SetValidatorByPubKeyIndex(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
if validator.Status == sdk.Bonded {

View File

@ -280,10 +280,6 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
panic(fmt.Sprintf("cliff validator record not found for address: %v\n", cliffAddr))
}
// NOTE: We get the power via affectedVal since the store (by power key)
// has yet to be updated.
affectedValPower := affectedVal.GetPower()
// Create a validator iterator ranging from smallest to largest by power
// starting the current cliff validator's power.
start := GetValidatorsByPowerIndexKey(oldCliffVal, pool)
@ -307,16 +303,19 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
panic("failed to create valid validator power iterator")
}
affectedValRank := GetValidatorsByPowerIndexKey(affectedVal, pool)
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
if bytes.Equal(affectedVal.Operator, newCliffVal.Operator) {
// The affected validator remains the cliff validator, however, since
// the store does not contain the new power, set the new cliff
// validator to the affected validator.
bz := GetValidatorsByPowerIndexKey(affectedVal, pool)
store.Set(ValidatorPowerCliffKey, bz)
} else if affectedValPower.GT(newCliffVal.GetPower()) {
// the store does not contain the new power, update the new power rank.
store.Set(ValidatorPowerCliffKey, affectedValRank)
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
// The affected validator no longer remains the cliff validator as it's
// power is greater than the new current cliff validator.
// power is greater than the new cliff validator.
k.setCliffValidator(ctx, newCliffVal, pool)
} else {
panic("invariant broken: the cliff validator should change or it should remain the same")
}
}
@ -417,13 +416,17 @@ func (k Keeper) UpdateBondedValidators(
}
}
if validator.Revoked {
// we should no longer consider jailed validators as they are ranked
// lower than any non-jailed/bonded validators
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("revoked validator cannot be bonded for address: %s\n", ownerAddr))
// increment bondedValidatorsCount / get the validator to bond
if !validator.Revoked {
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
} else {
// TODO: document why we must break here.
break
}
@ -440,6 +443,10 @@ func (k Keeper) UpdateBondedValidators(
iterator.Close()
if newValidatorBonded && bytes.Equal(oldCliffValidatorAddr, validator.Operator) {
panic("cliff validator has not been changed, yet we bonded a new validator")
}
// clear or set the cliff validator
if bondedValidatorsCount == int(maxValidators) {
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
@ -450,21 +457,29 @@ func (k Keeper) UpdateBondedValidators(
// swap the cliff validator for a new validator if the affected validator
// was bonded
if newValidatorBonded {
// unbond the cliff validator
if oldCliffValidatorAddr != nil {
cliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
}
k.unbondValidator(ctx, cliffVal)
if bytes.Equal(validatorToBond.Operator, affectedValidator.Operator) {
// unbond the old cliff validator iff the affected validator was
// newly bonded and has greater power
k.unbondValidator(ctx, oldCliffVal)
} else {
// otherwise unbond the affected validator, which must have been
// kicked out
affectedValidator = k.unbondValidator(ctx, affectedValidator)
}
}
// bond the new validator
validator = k.bondValidator(ctx, validatorToBond)
if bytes.Equal(validator.Operator, affectedValidator.Operator) {
return validator, true
}
return affectedValidator, true
}
return types.Validator{}, false
@ -560,7 +575,7 @@ func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) type
// sanity check
if validator.Status == sdk.Unbonded {
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
}
// set the status

View File

@ -48,7 +48,6 @@ func TestSetValidator(t *testing.T) {
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidator(), updates[0])
}
func TestUpdateValidatorByPowerIndex(t *testing.T) {
@ -89,6 +88,69 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
}
func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
numVals := 10
maxVals := 5
// create context, keeper, and pool for tests
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
// create keeper parameters
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(maxVals)
keeper.SetParams(ctx, params)
// create a random pool
pool.LooseTokens = sdk.NewDec(10000)
pool.BondedTokens = sdk.NewDec(1234)
keeper.SetPool(ctx, pool)
validators := make([]types.Validator, numVals)
for i := 0; i < len(validators); i++ {
moniker := fmt.Sprintf("val#%d", int64(i))
val := types.NewValidator(Addrs[i], PKs[i], types.Description{Moniker: moniker})
val.BondHeight = int64(i)
val.BondIntraTxCounter = int16(i)
val, pool, _ = val.AddTokensFromDel(pool, int64((i+1)*10))
keeper.SetPool(ctx, pool)
val = keeper.UpdateValidator(ctx, val)
validators[i] = val
}
nextCliffVal := validators[numVals-maxVals+1]
// remove enough tokens to kick out the validator below the current cliff
// validator and next in line cliff validator
nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21))
keeper.SetPool(ctx, pool)
nextCliffVal = keeper.UpdateValidator(ctx, nextCliffVal)
// require the cliff validator has changed
cliffVal := validators[numVals-maxVals-1]
require.Equal(t, cliffVal.Operator, sdk.AccAddress(keeper.GetCliffValidator(ctx)))
// require the cliff validator power has changed
cliffPower := keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(cliffVal, pool), cliffPower)
expectedValStatus := map[int]sdk.BondStatus{
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
0: sdk.Unbonded, 1: sdk.Unbonded, 2: sdk.Unbonded, 3: sdk.Unbonded, 6: sdk.Unbonded,
}
// require all the validators have their respective statuses
for valIdx, status := range expectedValStatus {
valAddr := validators[valIdx].Operator
val, _ := keeper.GetValidator(ctx, valAddr)
require.Equal(
t, val.GetStatus(), status,
fmt.Sprintf("expected validator to have status: %s", sdk.BondStatusToString(status)))
}
}
func TestCliffValidatorChange(t *testing.T) {
numVals := 10
maxVals := 5
@ -143,6 +205,19 @@ func TestCliffValidatorChange(t *testing.T) {
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, newCliffVal.Operator, sdk.AccAddress(keeper.GetCliffValidator(ctx)))
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
// add enough power to cliff validator to be equal in rank to next validator
newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, 9)
keeper.SetPool(ctx, pool)
newCliffVal = keeper.UpdateValidator(ctx, newCliffVal)
// assert new cliff validator due to power rank construction
newCliffVal = validators[numVals-maxVals+2]
require.Equal(t, newCliffVal.Operator, sdk.AccAddress(keeper.GetCliffValidator(ctx)))
// assert cliff validator power should have been updated
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
}
func TestSlashToZeroPowerRemoved(t *testing.T) {
@ -403,11 +478,13 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
var validators [4]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
moniker := fmt.Sprintf("val#%d", int64(i))
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
}
for i := range amts {
validators[i], found = keeper.GetValidator(ctx, validators[i].Operator)
require.True(t, found)

View File

@ -28,7 +28,7 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simula
func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simulation.Invariant {
return func(t *testing.T, app *baseapp.BaseApp, log string) {
ctx := app.NewContext(false, abci.Header{})
pool := k.GetPool(ctx)
//pool := k.GetPool(ctx)
loose := sdk.ZeroInt()
bonded := sdk.ZeroDec()
@ -52,11 +52,14 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim
})
// Loose tokens should equal coin supply plus unbonding delegations plus tokens on unbonded validators
require.True(t, pool.LooseTokens.RoundInt64() == loose.Int64(), "expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v\nlog: %s",
pool.LooseTokens.RoundInt64(), loose.Int64(), log)
// XXX TODO https://github.com/cosmos/cosmos-sdk/issues/2063#issuecomment-413720872
// require.True(t, pool.LooseTokens.RoundInt64() == loose.Int64(), "expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v\nlog: %s",
// pool.LooseTokens.RoundInt64(), loose.Int64(), log)
// Bonded tokens should equal sum of tokens with bonded validators
require.True(t, pool.BondedTokens.Equal(bonded), "expected bonded tokens to equal total steak held by bonded validators\nlog: %s", log)
// XXX TODO https://github.com/cosmos/cosmos-sdk/issues/2063#issuecomment-413720872
// require.True(t, pool.BondedTokens.RoundInt64() == bonded.RoundInt64(), "expected bonded tokens to equal total steak held by bonded validators - pool.BondedTokens: %v, sum of bonded validator tokens: %v\nlog: %s",
// pool.BondedTokens.RoundInt64(), bonded.RoundInt64(), log)
// TODO Inflation check on total supply
}

View File

@ -235,7 +235,10 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.TestAndRunTx {
func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
return func(r *rand.Rand, privKeys []crypto.PrivKey) {
ctx := mapp.NewContext(false, abci.Header{})
stake.InitGenesis(ctx, k, stake.DefaultGenesisState())
gen := stake.DefaultGenesisState()
gen.Params.InflationMax = sdk.NewDec(0)
gen.Params.InflationMin = sdk.NewDec(0)
stake.InitGenesis(ctx, k, gen)
params := k.GetParams(ctx)
denom := params.BondDenom
loose := sdk.ZeroInt()

View File

@ -5,12 +5,14 @@ import (
"math/rand"
"testing"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/tendermint/abci/types"
)
// TestStakeWithRandomMessages
@ -35,7 +37,7 @@ func TestStakeWithRandomMessages(t *testing.T) {
panic(err)
}
appStateFn := func(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage {
appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
mock.RandomSetGenesis(r, mapp, accs, []string{"stake"})
return json.RawMessage("{}")
}
@ -54,6 +56,7 @@ func TestStakeWithRandomMessages(t *testing.T) {
Setup(mapp, stakeKeeper),
}, []simulation.Invariant{
AllInvariants(coinKeeper, stakeKeeper, mapp.AccountMapper),
}, 10, 100, 100,
}, 10, 100,
false,
)
}

View File

@ -2,9 +2,11 @@ package types
import (
"bytes"
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// defaultUnbondingTime reflects three weeks in seconds as the default
@ -43,3 +45,36 @@ func DefaultParams() Params {
BondDenom: "steak",
}
}
// HumanReadableString returns a human readable string representation of the
// parameters.
func (p Params) HumanReadableString() string {
resp := "Pool \n"
resp += fmt.Sprintf("Maximum Annual Inflation Rate Change: %s\n", p.InflationRateChange)
resp += fmt.Sprintf("Max Inflation Rate: %s\n", p.InflationMax)
resp += fmt.Sprintf("Min Inflation Tate: %s\n", p.InflationMin)
resp += fmt.Sprintf("Bonded Token Goal (%s): %s\n", "s", p.GoalBonded)
resp += fmt.Sprintf("Unbonding Time: %s\n", p.UnbondingTime)
resp += fmt.Sprintf("Max Validators: %d: \n", p.MaxValidators)
resp += fmt.Sprintf("Bonded Coin Denomination: %s\n", p.BondDenom)
return resp
}
// unmarshal the current staking params value from store key or panic
func MustUnmarshalParams(cdc *wire.Codec, value []byte) Params {
params, err := UnmarshalParams(cdc, value)
if err != nil {
panic(err)
}
return params
}
// unmarshal the current staking params value from store key
func UnmarshalParams(cdc *wire.Codec, value []byte) (params Params, err error) {
err = cdc.UnmarshalBinary(value, &params)
if err != nil {
return
}
return
}

Some files were not shown because too many files have changed in this diff Show More