Merge pull request #1991 from cosmos/release/v0.24.0

Release/v0.24.0
This commit is contained in:
Ethan Buchman 2018-08-19 15:51:21 -04:00 committed by GitHub
commit d5652d9676
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
422 changed files with 18430 additions and 20264 deletions

View File

@ -62,7 +62,7 @@ jobs:
name: Get metalinter
command: |
export PATH="$GOBIN:$PATH"
make get_tools
make get_dev_tools
- run:
name: Lint source
command: |
@ -85,6 +85,54 @@ jobs:
export PATH="$GOBIN:$PATH"
make test_cli
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 }}
- 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
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- 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
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Test full Gaia simulation
command: |
export PATH="$GOBIN:$PATH"
make test_sim_gaia_fast
test_cover:
<<: *defaults
parallelism: 4
@ -101,9 +149,9 @@ jobs:
command: |
export PATH="$GOBIN:$PATH"
make install
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | 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 -v -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
GOCACHE=off go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
done
- persist_to_workspace:
root: /tmp/workspace
@ -133,6 +181,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:
@ -144,9 +215,21 @@ workflows:
- test_cli:
requires:
- setup_dependencies
- 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

@ -1,16 +1,17 @@
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v ✰ Thanks for creating a PR! ✰
v Before smashing the submit button please review the checkboxes.
v Before smashing the submit button please review the checkboxes.
v If a checkbox is n/a - please still include it but + a little note why
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
* [ ] Updated all relevant documentation (`docs/`)
* [ ] Updated all relevant code comments
* [ ] Wrote tests
* [ ] Updated `CHANGELOG.md`
* [ ] Updated `cmd/gaia` and `examples/`
- [ ] 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/`
___________________________________
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,119 @@
# Changelog
## 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*
@ -20,7 +134,7 @@ IMPROVEMENTS
* [cli] Improve error messages for all txs when the account doesn't exist
* [tendermint] Update to v0.22.6
- Updates the crypto imports/API (#1966)
* [x/stake] Add revoked to human-readable validator
* [x/stake] Add revoked to human-readable validator
BUG FIXES
* [tendermint] Update to v0.22.6
@ -77,6 +191,8 @@ BUG FIXES
* [keys] \#1629 - updating password no longer asks for a new password when the first entered password was incorrect
* [lcd] importing an account would create a random account
* [server] 'gaiad init' command family now writes provided name as the moniker in `config.toml`
* [build] Added Ledger build support via `LEDGER_ENABLED=true|false`
* True by default except when cross-compiling
## 0.20.0
@ -165,7 +281,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

View File

@ -1,18 +1,52 @@
# Contributing
Thank you for considering making contributions to Cosmos-SDK and related repositories! Start by taking a look at this [coding repo](https://github.com/tendermint/coding) for overall information on repository workflow and standards.
Thank you for considering making contributions to Cosmos-SDK and related
repositories!
Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/cosmos/cosmos-sdk/issues) for things we need help with!
Contributing to this repo can mean many things such as participated in
discussion or proposing code changes. To ensure a smooth workflow for all
contributors, the general procedure for contributing has been established:
Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint`
1. either [open](https://github.com/cosmos/cosmos-sdk/issues/new/choose) or
[find](https://github.com/cosmos/cosmos-sdk/issues) an issue you'd like to help with,
2. participate in thoughtful discussion on that issue,
3. if you would then like to contribute code:
1. if a the issue is a proposal, ensure that the proposal has been accepted,
2. ensure that nobody else has already begun working on this issue, if they have
make sure to contact them to collaborate,
3. if nobody has been assigned the issue and you would like to work on it
make a comment on the issue to inform the community of your intentions
to begin work,
4. follow standard github best practices: fork the repo, branch from the
tip of `develop`, make some commits, and submit a PR to `develop`,
5. include `WIP:` in the PR-title to and submit your PR early, even if it's
incomplete, this indicates to the community you're working on something and
allows them to provide comments early in the development process. When the code
is complete it can be marked as ready-for-review by replacing `WIP:` with
`R4R:` in the PR-title.
Looking for a good place to start contributing? How about checking out some [good first issues](https://github.com/cosmos/cosmos-sdk/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
Note that for very small or blatantly obvious problems (such as typos) it is
not required to an open issue to submit a PR, but be aware that for more complex
problems/features, if a PR is opened before an adequate design discussion has
taken place in a github issue, that PR runs a high likelihood of being rejected.
Take a peek at our [coding repo](https://github.com/tendermint/coding) for
overall information on repository workflow and standards. Note, we use `make
get_dev_tools` and `make update_dev_tools` for installing the linting tools.
Other notes:
- Looking for a good place to start contributing? How about checking out some
[good first
issues](https://github.com/cosmos/cosmos-sdk/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
- Please make sure to use `gofmt` before every commit - the easiest way to do
this is have your editor run it for you upon saving a file. Additionally
please ensure that your code is lint compliant by running `make lint`
## Pull Requests
To accommodate review process we suggest that PRs are catagorically broken up.
To accommodate review process we suggest that PRs are categorically broken up.
Ideally each PR addresses only a single issue. Additionally, as much as possible
code refactoring and cleanup should be submitted as a seperate PRs from bugfixes/feature-additions.
code refactoring and cleanup should be submitted as a separate PRs from bugfixes/feature-additions.
## Forking
@ -24,10 +58,10 @@ Instead, we use `git remote` to add the fork as a new remote for the original re
For instance, to create a fork and work on a branch of it, I would:
* Create the fork on github, using the fork button.
* Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/cosmos-sdk`)
* `git remote rename origin upstream`
* `git remote add origin git@github.com:ebuchman/basecoin.git`
- Create the fork on github, using the fork button.
- Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/cosmos-sdk`)
- `git remote rename origin upstream`
- `git remote add origin git@github.com:ebuchman/basecoin.git`
Now `origin` refers to my fork and `upstream` refers to the Cosmos-SDK version.
So I can `git push -u origin master` to update my fork, and make pull requests to Cosmos-SDK from there.
@ -35,8 +69,8 @@ Of course, replace `ebuchman` with your git handle.
To pull in updates from the origin repo, run
* `git fetch upstream`
* `git rebase upstream/master` (or whatever branch you want)
- `git fetch upstream`
- `git rebase upstream/master` (or whatever branch you want)
Please don't make Pull Requests to `master`.
@ -67,6 +101,29 @@ tested by circle using `go test -v -race ./...`. If not, they will need a
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
includes its continuous integration status using a badge in the `README.md`.
We expect tests to use `require` or `assert` rather than `t.Skip` or `t.Fail`,
unless there is a reason to do otherwise.
When testing a function under a variety of different inputs, we prefer to use
[table driven tests](https://github.com/golang/go/wiki/TableDrivenTests).
Table driven test error messages should follow the following format
`<desc>, tc #<index>, i #<index>`.
`<desc>` is an optional short description of whats failing, `tc` is the
index within the table of the testcase that is failing, and `i` is when there
is a loop, exactly which iteration of the loop failed.
The idea is you should be able to see the
error message and figure out exactly what failed.
Here is an example check:
```
<some table>
for tcIndex, tc := range cases {
<some code>
for i := 0; i < tc.numTxsToTest; i++ {
<some code>
require.Equal(t, expectedTx[:32], calculatedTx[:32],
"First 32 bytes of the txs differed. tc #%d, i #%d", tcIndex, i)
```
## Branching Model and Release
User-facing repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
@ -77,35 +134,35 @@ Libraries need not follow the model strictly, but would be wise to.
The SDK utilizes [semantic versioning](https://semver.org/).
### Development Procedure:
- the latest state of development is on `develop`
- `develop` must never fail `make test` or `make test_cli`
- `develop` should not fail `make test_lint`
- no --force onto `develop` (except when reverting a broken commit, which should seldom happen)
- create a development branch either on github.com/cosmos/cosmos-sdk, or your fork (using `git remote add origin`)
- before submitting a pull request, begin `git rebase` on top of `develop`
- the latest state of development is on `develop`
- `develop` must never fail `make test` or `make test_cli`
- `develop` should not fail `make test_lint`
- no --force onto `develop` (except when reverting a broken commit, which should seldom happen)
- create a development branch either on github.com/cosmos/cosmos-sdk, or your fork (using `git remote add origin`)
- before submitting a pull request, begin `git rebase` on top of `develop`
### Pull Merge Procedure:
- ensure pull branch is rebased on develop
- run `make test` and `make test_cli` to ensure that all tests pass
- merge pull request
- push master may request that pull requests be rebased on top of `unstable`
- ensure pull branch is rebased on develop
- run `make test` and `make test_cli` to ensure that all tests pass
- merge pull request
- push master may request that pull requests be rebased on top of `unstable`
### Release Procedure:
- start on `develop`
- prepare changelog/release issue
- bump versions
- push to release-vX.X.X to run CI
- merge to master
- merge master back to develop
- start on `develop`
- prepare changelog/release issue
- bump versions
- push to release-vX.X.X to run CI
- merge to master
- merge master back to develop
### Hotfix Procedure:
- start on `master`
- checkout a new branch named hotfix-vX.X.X
- make the required changes
- these changes should be small and an absolute necessity
- add a note to CHANGELOG.md
- bump versions
- push to hotfix-vX.X.X to run the extended integration tests on the CI
- merge hotfix-vX.X.X to master
- merge hotfix-vX.X.X to develop
- delete the hotfix-vX.X.X branch
- start on `master`
- checkout a new branch named hotfix-vX.X.X
- make the required changes
- these changes should be small and an absolute necessity
- add a note to CHANGELOG.md
- bump versions
- push to hotfix-vX.X.X to run the extended integration tests on the CI
- merge hotfix-vX.X.X to master
- merge hotfix-vX.X.X to develop
- delete the hotfix-vX.X.X branch

230
Gopkg.lock generated
View File

@ -2,58 +2,76 @@
[[projects]]
branch = "master"
digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0"
name = "github.com/bartekn/go-bip39"
packages = ["."]
pruneopts = "UT"
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
[[projects]]
branch = "master"
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "UT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:1343a2963481a305ca4d051e84bc2abd16b601ee22ed324f8d605de1adb291b0"
name = "github.com/bgentry/speakeasy"
packages = ["."]
pruneopts = "UT"
revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd"
version = "v0.1.0"
[[projects]]
branch = "master"
digest = "1:70f6b224a59b2fa453debffa85c77f71063d8754b90c8c4fbad5794e2c382b0f"
name = "github.com/brejski/hid"
packages = ["."]
pruneopts = "UT"
revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc"
[[projects]]
branch = "master"
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "9a2f9524024889e129a5422aca2cff73cb3eabf6"
pruneopts = "UT"
revision = "f899737d7f2764dc13e4d01ff00108ec58f766a9"
[[projects]]
digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2"
name = "github.com/btcsuite/btcutil"
packages = ["bech32"]
pruneopts = "UT"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
name = "github.com/ebuchman/fail-test"
packages = ["."]
pruneopts = "UT"
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "UT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
name = "github.com/go-kit/kit"
packages = [
"log",
@ -62,24 +80,30 @@
"metrics",
"metrics/discard",
"metrics/internal/lv",
"metrics/prometheus"
"metrics/prometheus",
]
pruneopts = "UT"
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659"
name = "github.com/go-logfmt/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406"
name = "github.com/go-stack/stack"
packages = ["."]
pruneopts = "UT"
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
@ -87,213 +111,272 @@
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types"
"types",
]
pruneopts = "UT"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1"
[[projects]]
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
"ptypes/timestamp",
]
pruneopts = "UT"
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = "UT"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
name = "github.com/gorilla/context"
packages = ["."]
pruneopts = "UT"
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f"
name = "github.com/gorilla/mux"
packages = ["."]
pruneopts = "UT"
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
name = "github.com/gorilla/websocket"
packages = ["."]
pruneopts = "UT"
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
"json/token",
]
pruneopts = "UT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = "UT"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
branch = "master"
digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214"
name = "github.com/jmhodges/levigo"
packages = ["."]
pruneopts = "UT"
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72"
name = "github.com/kr/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
name = "github.com/magiconair/properties"
packages = ["."]
pruneopts = "UT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "UT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
branch = "master"
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/promhttp"
"prometheus/promhttp",
]
pruneopts = "UT"
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
pruneopts = "UT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
branch = "master"
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model"
"model",
]
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
pruneopts = "UT"
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
[[projects]]
branch = "master"
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs"
"xfs",
]
revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a"
pruneopts = "UT"
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
[[projects]]
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
pruneopts = "UT"
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
name = "github.com/spf13/afero"
packages = [
".",
"mem"
"mem",
]
pruneopts = "UT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "UT"
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
[[projects]]
branch = "master"
digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
pruneopts = "UT"
revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2"
[[projects]]
digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9"
name = "github.com/spf13/pflag"
packages = ["."]
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
pruneopts = "UT"
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]]
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "UT"
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
version = "v1.0.0"
[[projects]]
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
"require",
]
pruneopts = "UT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
@ -307,33 +390,41 @@
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util"
"leveldb/util",
]
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
pruneopts = "UT"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
[[projects]]
branch = "master"
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519"
"extra25519",
]
pruneopts = "UT"
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
pruneopts = "UT"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
[[projects]]
digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2"
name = "github.com/tendermint/iavl"
packages = ["."]
pruneopts = "UT"
revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9"
version = "v0.9.2"
[[projects]]
digest = "1:26146cdb2811ce481e72138439b9b1aa17a64d54364f96bb92f97a9ef8ba4f01"
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
@ -393,22 +484,29 @@
"state/txindex/kv",
"state/txindex/null",
"types",
"version"
"version",
]
revision = "d542d2c3945116697f60451e6a407082c41c3cc9"
version = "v0.22.8-rc0"
pruneopts = "UT"
revision = "013b9cef642f875634c614019ab13b17570778ad"
version = "v0.23.0"
[[projects]]
digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6"
name = "github.com/zondax/ledger-goclient"
packages = ["."]
revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b"
pruneopts = "UT"
revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259"
[[projects]]
branch = "master"
digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5"
name = "golang.org/x/crypto"
packages = [
"blowfish",
"chacha20poly1305",
"curve25519",
"hkdf",
"internal/chacha20",
"internal/subtle",
"nacl/box",
"nacl/secretbox",
@ -417,11 +515,13 @@
"pbkdf2",
"poly1305",
"ripemd160",
"salsa20/salsa"
"salsa20/salsa",
]
revision = "c126467f60eb25f8f27e5a981f32a87e3965053f"
pruneopts = "UT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
name = "golang.org/x/net"
packages = [
"context",
@ -431,17 +531,24 @@
"idna",
"internal/timeseries",
"netutil",
"trace"
"trace",
]
pruneopts = "UT"
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
[[projects]]
branch = "master"
digest = "1:a989b95f72fce8876213e8e20492525b4cf69a9e7fee7f1d9897983ee0d547e9"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text"
packages = [
"collate",
@ -457,18 +564,22 @@
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
"unicode/rangetable",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "02b4e95473316948020af0b7a4f0f22c73929b0e"
pruneopts = "UT"
revision = "d0a8f471bba2dbb160885b0000d814ee5d559bad"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
name = "google.golang.org/grpc"
packages = [
".",
@ -495,20 +606,69 @@
"stats",
"status",
"tap",
"transport"
"transport",
]
pruneopts = "UT"
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
version = "v1.13.0"
[[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "93154e6c678a7bfa273eccf7222531922d644999974efe347893bdd8ba24b14b"
input-imports = [
"github.com/bartekn/go-bip39",
"github.com/bgentry/speakeasy",
"github.com/btcsuite/btcd/btcec",
"github.com/golang/protobuf/proto",
"github.com/gorilla/mux",
"github.com/mattn/go-isatty",
"github.com/pkg/errors",
"github.com/spf13/cobra",
"github.com/spf13/pflag",
"github.com/spf13/viper",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"github.com/tendermint/go-amino",
"github.com/tendermint/iavl",
"github.com/tendermint/tendermint/abci/server",
"github.com/tendermint/tendermint/abci/types",
"github.com/tendermint/tendermint/cmd/tendermint/commands",
"github.com/tendermint/tendermint/config",
"github.com/tendermint/tendermint/crypto",
"github.com/tendermint/tendermint/crypto/armor",
"github.com/tendermint/tendermint/crypto/ed25519",
"github.com/tendermint/tendermint/crypto/encoding/amino",
"github.com/tendermint/tendermint/crypto/merkle",
"github.com/tendermint/tendermint/crypto/secp256k1",
"github.com/tendermint/tendermint/crypto/tmhash",
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric",
"github.com/tendermint/tendermint/libs/bech32",
"github.com/tendermint/tendermint/libs/cli",
"github.com/tendermint/tendermint/libs/cli/flags",
"github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log",
"github.com/tendermint/tendermint/node",
"github.com/tendermint/tendermint/p2p",
"github.com/tendermint/tendermint/privval",
"github.com/tendermint/tendermint/proxy",
"github.com/tendermint/tendermint/rpc/client",
"github.com/tendermint/tendermint/rpc/core/types",
"github.com/tendermint/tendermint/rpc/lib/client",
"github.com/tendermint/tendermint/rpc/lib/server",
"github.com/tendermint/tendermint/types",
"github.com/tendermint/tendermint/version",
"github.com/zondax/ledger-goclient",
"golang.org/x/crypto/blowfish",
"golang.org/x/crypto/ripemd160",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -10,11 +10,6 @@
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
@ -54,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"
@ -62,15 +57,15 @@
[[override]]
name = "github.com/tendermint/tendermint"
version = "=v0.22.8"
version = "=v0.23.0"
[[constraint]]
name = "github.com/bartekn/go-bip39"
branch = "master"
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
[[constraint]]
name = "github.com/zondax/ledger-goclient"
revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b"
revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259"
[prune]
go-tests = true

View File

@ -1,11 +1,11 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
PACKAGES_NOCLITEST=$(shell go list ./... | grep -v '/vendor/' | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test)
PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation')
PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation')
COMMIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_TAGS = netgo ledger
BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
GCC := $(shell command -v gcc 2> /dev/null)
LEDGER_ENABLED ?= true
all: get_tools get_vendor_deps install install_examples test_lint test
all: get_tools get_vendor_deps install install_examples install_cosmos-sdk-cli test_lint test
########################################
### CI
@ -15,7 +15,7 @@ ci: get_tools get_vendor_deps install test_cover test_lint test
########################################
### Build/Install
check-ledger:
check-ledger:
ifeq ($(LEDGER_ENABLED),true)
ifndef GCC
$(error "gcc not installed for ledger support, please install")
@ -37,6 +37,13 @@ endif
build-linux:
LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build
build_cosmos-sdk-cli:
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli
else
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli ./cmd/cosmos-sdk-cli
endif
build_examples:
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind
@ -60,6 +67,9 @@ install_examples:
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democli
install_cosmos-sdk-cli:
go install $(BUILD_FLAGS) ./cmd/cosmos-sdk-cli
install_debug:
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug
@ -73,14 +83,22 @@ dist:
check_tools:
cd tools && $(MAKE) check_tools
check_dev_tools:
cd tools && $(MAKE) check_dev_tools
update_tools:
cd tools && $(MAKE) update_tools
update_dev_tools:
cd tools && $(MAKE) update_dev_tools
get_tools:
cd tools && $(MAKE) get_tools
get_dev_tools:
cd tools && $(MAKE) get_dev_tools
get_vendor_deps:
@rm -rf vendor/
@echo "--> Running dep ensure"
@dep ensure -v
@ -104,13 +122,29 @@ godocs:
test: test_unit
test_cli:
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test`
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -tags=cli_test
test_unit:
@go test $(PACKAGES_NOCLITEST)
@go test $(PACKAGES_NOSIMULATION)
test_race:
@go test -race $(PACKAGES_NOCLITEST)
@go test -race $(PACKAGES_NOSIMULATION)
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
@ -119,13 +153,15 @@ test_lint:
gometalinter.v2 --config=tools/gometalinter.json ./...
!(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
dep status >> /dev/null
!(grep -n branch Gopkg.toml)
format:
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -w -s
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w
benchmark:
@go test -bench=. $(PACKAGES_NOCLITEST)
@go test -bench=. $(PACKAGES_NOSIMULATION)
########################################
@ -161,38 +197,17 @@ 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
docker-compose up -d
# Stop testnet
localnet-stop:
docker-compose down
########################################
### Remote validator nodes using terraform and ansible
TESTNET_NAME?=remotenet
SERVERS?=4
BINARY=$(CURDIR)/build/gaiad
remotenet-start:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
@if ! [ -f $(HOME)/.ssh/id_rsa.pub ]; then ssh-keygen ; fi
@if [ -z "`file $(BINARY) | grep 'ELF 64-bit'`" ]; then echo "Please build a linux binary using 'make build-linux'." ; false ; fi
cd networks/remote/terraform && terraform init && terraform apply -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_PUBLIC_FILE="$(HOME)/.ssh/id_rsa.pub" -var SSH_PRIVATE_FILE="$(HOME)/.ssh/id_rsa" -var TESTNET_NAME="$(TESTNET_NAME)" -var SERVERS="$(SERVERS)"
cd networks/remote/ansible && ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory/digital_ocean.py -l "$(TESTNET_NAME)" -e BINARY=$(BINARY) -e TESTNET_NAME="$(TESTNET_NAME)" setup-validators.yml
cd networks/remote/ansible && ansible-playbook -i inventory/digital_ocean.py -l "$(TESTNET_NAME)" start.yml
remotenet-stop:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_PUBLIC_FILE="$(HOME)/.ssh/id_rsa.pub" -var SSH_PRIVATE_FILE="$(HOME)/.ssh/id_rsa"
remotenet-status:
cd networks/remote/ansible && ansible-playbook -i inventory/digital_ocean.py -l "$(TESTNET_NAME)" status.yml
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: build build_examples install install_examples install_debug dist \
check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit \
.PHONY: build build_cosmos-sdk-cli build_examples install install_examples install_cosmos-sdk-cli install_debug dist \
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 remotenet-start \
remotenet-stop remotenet-status format check-ledger
build-linux build-docker-gaiadnode localnet-start localnet-stop \
format check-ledger test_sim_modules test_sim_gaia_fast test_sim_gaia_slow update_tools update_dev_tools

View File

@ -1,41 +1,53 @@
## PENDING
BREAKING CHANGES
* [baseapp] Msgs are no longer run on CheckTx, removed `ctx.IsCheckTx()`
* [x/gov] CLI flag changed from `proposalID` to `proposal-id`
* [x/stake] Inflation doesn't use rationals in calculation (performance boost)
* [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
* Gaia REST API (`gaiacli advanced rest-server`)
* Gaia CLI (`gaiacli`)
* Gaia
* Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013)
* SDK
* 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)
* Gaia REST API (`gaiacli advanced rest-server`)
* Gaia CLI (`gaiacli`)
* Gaia
* SDK
* Tendermint
IMPROVEMENTS
* [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
* [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
* Gaia REST API (`gaiacli advanced rest-server`)
* Gaia CLI (`gaiacli`)
* Gaia
* SDK
* Tendermint
BUG FIXES
* Gaia REST API (`gaiacli advanced rest-server`)
* Gaia CLI (`gaiacli`)
* Gaia
* SDK
* Tendermint

View File

@ -36,11 +36,9 @@ See the
See the [Cosmos Docs](https://cosmos.network/docs/)
- [Getting started with the
SDK](https://cosmos.network/docs/sdk/core/intro.html)
- [Getting started with the SDK](https://cosmos.network/docs/sdk/core/intro.html)
- [SDK Examples](/examples)
- [Join the
testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node)
- [Join the testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node)
## Disambiguation

View File

@ -18,7 +18,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// Key to store the header in the DB itself.
@ -44,14 +43,12 @@ type BaseApp struct {
// initialized on creation
Logger log.Logger
name string // application name from abci.Info
cdc *wire.Codec // Amino codec
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
// must be set
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
anteHandler sdk.AnteHandler // ante handler for fee and auth
// may be nil
@ -69,6 +66,9 @@ type BaseApp struct {
checkState *state // for CheckTx
deliverState *state // for DeliverTx
signedValidators []abci.SigningValidator // absent validators from begin block
// flag for sealing
sealed bool
}
var _ abci.Application = (*BaseApp)(nil)
@ -80,17 +80,17 @@ var _ abci.Application = (*BaseApp)(nil)
// (e.g. functional options).
//
// NOTE: The db is used to store the version number for now.
// Accepts a user-defined txDecoder
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, options ...func(*BaseApp)) *BaseApp {
func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp)) *BaseApp {
app := &BaseApp{
Logger: logger,
name: name,
cdc: cdc,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
codespacer: sdk.NewCodespacer(),
txDecoder: defaultTxDecoder(cdc),
txDecoder: txDecoder,
}
// Register the undefined & root codespaces, which should not be used by
@ -135,56 +135,6 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
app.cms.MountStoreWithDB(key, typ, nil)
}
// Set the txDecoder function
func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
app.txDecoder = txDecoder
}
// default custom logic for transaction decoding
// TODO: remove auth and wire dependencies from baseapp
// - move this to auth.DefaultTxDecoder
// - set the default here to JSON decode like docs/examples/app1 (it will fail
// for multiple messages ;))
// - pass a TxDecoder into NewBaseApp, instead of a codec.
func defaultTxDecoder(cdc *wire.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = auth.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
}
return tx, nil
}
}
// nolint - Set functions
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
app.initChainer = initChainer
}
func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) {
app.beginBlocker = beginBlocker
}
func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
app.endBlocker = endBlocker
}
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
app.anteHandler = ah
}
func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) {
app.addrPeerFilter = pf
}
func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) {
app.pubkeyPeerFilter = pf
}
func (app *BaseApp) Router() Router { return app.router }
// load latest application version
func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error {
err := app.cms.LoadLatestVersion()
@ -215,13 +165,16 @@ func (app *BaseApp) LastBlockHeight() int64 {
// initializes the remaining logic from app.cms
func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
// main store should exist.
// TODO: we don't actually need the main store here
main := app.cms.GetKVStore(mainKey)
if main == nil {
return errors.New("baseapp expects MultiStore with 'main' KVStore")
}
// Needed for `gaiad export`, which inits from store but never calls initchain
app.setCheckState(abci.Header{})
app.Seal()
return nil
}
@ -290,7 +243,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
if app.initChainer == nil {
return
}
app.initChainer(app.deliverState.ctx, req) // no error
res = app.initChainer(app.deliverState.ctx, req)
// NOTE: we don't commit, but BeginBlock for block 1
// starts from this deliverState
@ -364,7 +317,9 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
default:
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
}
value := app.cdc.MustMarshalBinary(result)
// Encode with json
value := wire.Cdc.MustMarshalBinary(result)
return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK),
Value: value,
@ -424,7 +379,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 {
@ -432,11 +387,16 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
}
// set the signed validators for addition to context in deliverTx
app.signedValidators = req.Validators
// TODO: communicate this result to the address to pubkey map in slashing
app.signedValidators = req.LastCommitInfo.GetValidators()
return
}
// Implements ABCI
// CheckTx implements ABCI
// CheckTx runs the "basic checks" to see whether or not a transaction can possibly be executed,
// first decoding, then the ante handler (which checks signatures/fees/ValidateBasic),
// then finally the route match to see whether a handler exists. CheckTx does not run the actual
// Msg handler function(s).
func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
// Decode the Tx.
var result sdk.Result
@ -453,11 +413,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
Log: result.Log,
GasWanted: result.GasWanted,
GasUsed: result.GasUsed,
Fee: cmn.KI64Pair{
[]byte(result.FeeDenom),
result.FeeAmount,
},
Tags: result.Tags,
Tags: result.Tags,
}
}
@ -514,16 +470,11 @@ func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.C
ctx = ctx.WithSigningValidators(app.signedValidators)
}
// Simulate a DeliverTx for gas calculation
if mode == runTxModeSimulate {
ctx = ctx.WithIsCheckTx(false)
}
return
}
// Iterates through msgs and executes them
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg) (result sdk.Result) {
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
// accumulate results
logs := make([]string, 0, len(msgs))
var data []byte // NOTE: we just append them all (?!)
@ -537,7 +488,11 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg) (result sdk.Result)
return sdk.ErrUnknownRequest("Unrecognized Msg type: " + msgType).Result()
}
msgResult := handler(ctx, msg)
var msgResult sdk.Result
// Skip actual execution for CheckTx
if mode != runTxModeCheck {
msgResult = handler(ctx, msg)
}
// NOTE: GasWanted is determined by ante handler and
// GasUsed by the GasMeter
@ -615,9 +570,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
// run the ante handler
if app.anteHandler != nil {
newCtx, anteResult, abort := app.anteHandler(ctx, tx)
newCtx, result, abort := app.anteHandler(ctx, tx)
if abort {
return anteResult
return result
}
if !newCtx.IsZero() {
ctx = newCtx
@ -636,7 +591,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
}
ctx = ctx.WithMultiStore(msCache)
result = app.runMsgs(ctx, msgs)
result = app.runMsgs(ctx, msgs, mode)
result.GasWanted = gasWanted
// only update state if all messages pass and we're not in a simulation

View File

@ -18,6 +18,12 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
)
var (
// make some cap keys
capKey1 = sdk.NewKVStoreKey("key1")
capKey2 = sdk.NewKVStoreKey("key2")
)
//------------------------------------------------------------------------------------------
// Helpers for setup. Most tests should be able to use setupBaseApp
@ -25,12 +31,12 @@ func defaultLogger() log.Logger {
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
}
func newBaseApp(name string) *BaseApp {
func newBaseApp(name string, options ...func(*BaseApp)) *BaseApp {
logger := defaultLogger()
db := dbm.NewMemDB()
codec := wire.NewCodec()
registerTestCodec(codec)
return NewBaseApp(name, codec, logger, db)
return NewBaseApp(name, logger, db, testTxDecoder(codec), options...)
}
func registerTestCodec(cdc *wire.Codec) {
@ -45,16 +51,10 @@ func registerTestCodec(cdc *wire.Codec) {
}
// simple one store baseapp
func setupBaseApp(t *testing.T) (*BaseApp, *sdk.KVStoreKey, *sdk.KVStoreKey) {
app := newBaseApp(t.Name())
func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp {
app := newBaseApp(t.Name(), options...)
require.Equal(t, t.Name(), app.Name())
app.SetTxDecoder(testTxDecoder(app.cdc))
// make some cap keys
capKey1 := sdk.NewKVStoreKey("key1")
capKey2 := sdk.NewKVStoreKey("key2")
// no stores are mounted
require.Panics(t, func() { app.LoadLatestVersion(capKey1) })
@ -63,14 +63,14 @@ func setupBaseApp(t *testing.T) (*BaseApp, *sdk.KVStoreKey, *sdk.KVStoreKey) {
// stores are mounted
err := app.LoadLatestVersion(capKey1)
require.Nil(t, err)
return app, capKey1, capKey2
return app
}
//------------------------------------------------------------------------------------------
// test mounting and loading stores
func TestMountStores(t *testing.T) {
app, capKey1, capKey2 := setupBaseApp(t)
app := setupBaseApp(t)
// check both stores
store1 := app.cms.GetCommitKVStore(capKey1)
@ -85,7 +85,7 @@ func TestLoadVersion(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()
app := NewBaseApp(name, nil, logger, db)
app := NewBaseApp(name, logger, db, nil)
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
@ -114,7 +114,7 @@ func TestLoadVersion(t *testing.T) {
commitID2 := sdk.CommitID{2, res.Data}
// reload with LoadLatestVersion
app = NewBaseApp(name, nil, logger, db)
app = NewBaseApp(name, logger, db, nil)
app.MountStoresIAVL(capKey)
err = app.LoadLatestVersion(capKey)
require.Nil(t, err)
@ -122,7 +122,7 @@ func TestLoadVersion(t *testing.T) {
// reload with LoadVersion, see if you can commit the same block and get
// the same result
app = NewBaseApp(name, nil, logger, db)
app = NewBaseApp(name, logger, db, nil)
app.MountStoresIAVL(capKey)
err = app.LoadVersion(1, capKey)
require.Nil(t, err)
@ -142,9 +142,7 @@ func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, exp
func TestOptionFunction(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
codec := wire.NewCodec()
registerTestCodec(codec)
bap := NewBaseApp("starting name", codec, logger, db, testChangeNameHelper("new name"))
bap := NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name"))
require.Equal(t, bap.name, "new name", "BaseApp should have had name changed via option function")
}
@ -216,12 +214,10 @@ func TestInitChainer(t *testing.T) {
// we can reload the same app later
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, nil, logger, db)
app := NewBaseApp(name, logger, db, nil)
capKey := sdk.NewKVStoreKey("main")
capKey2 := sdk.NewKVStoreKey("key2")
app.MountStoresIAVL(capKey, capKey2)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
require.Nil(t, err)
// set a value in the store on init chain
key, value := []byte("hello"), []byte("goodbye")
@ -243,6 +239,11 @@ func TestInitChainer(t *testing.T) {
// set initChainer and try again - should see the value
app.SetInitChainer(initChainer)
// stores are mounted and private members are set - sealing baseapp
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
require.Nil(t, err)
app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty
// assert that chainID is set correctly in InitChain
@ -257,11 +258,11 @@ func TestInitChainer(t *testing.T) {
require.Equal(t, value, res.Value)
// reload app
app = NewBaseApp(name, nil, logger, db)
app = NewBaseApp(name, logger, db, nil)
app.SetInitChainer(initChainer)
app.MountStoresIAVL(capKey, capKey2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
require.Nil(t, err)
app.SetInitChainer(initChainer)
// ensure we can still query after reloading
res = app.Query(query)
@ -290,7 +291,7 @@ func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs }
const (
typeMsgCounter = "msgCounter"
typeMsgCounter2 = "msgCounterTwo" // NOTE: no numerics (?)
typeMsgCounter2 = "msgCounter2"
)
// ValidateBasic() fails on negative counters.
@ -430,29 +431,35 @@ func incrementingCounter(t *testing.T, store sdk.KVStore, counterKey []byte, cou
// on the store within a block, and that the CheckTx state
// gets reset to the latest committed state during Commit
func TestCheckTx(t *testing.T) {
app, capKey, _ := setupBaseApp(t)
// This ante handler reads the key and checks that the value matches the current counter.
// This ensures changes to the kvstore persist across successive CheckTx.
counterKey := []byte("counter-key")
app.SetAnteHandler(anteHandlerTxTest(t, capKey, counterKey))
anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, counterKey)) }
routerOpt := func(bapp *BaseApp) {
// TODO: can remove this once CheckTx doesnt process msgs.
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return sdk.Result{} })
}
app := setupBaseApp(t, anteOpt, routerOpt)
nTxs := int64(5)
// TODO: can remove this once CheckTx doesnt process msgs.
app.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return sdk.Result{} })
app.InitChain(abci.RequestInitChain{})
// Create same codec used in txDecoder
codec := wire.NewCodec()
registerTestCodec(codec)
for i := int64(0); i < nTxs; i++ {
tx := newTxCounter(i, 0)
txBytes, err := app.cdc.MarshalBinary(tx)
txBytes, err := codec.MarshalBinary(tx)
require.NoError(t, err)
r := app.CheckTx(txBytes)
assert.True(t, r.IsOK(), fmt.Sprintf("%v", r))
}
checkStateStore := app.checkState.ctx.KVStore(capKey)
checkStateStore := app.checkState.ctx.KVStore(capKey1)
storedCounter := getIntFromStore(checkStateStore, counterKey)
// Ensure AnteHandler ran
@ -463,7 +470,7 @@ func TestCheckTx(t *testing.T) {
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
checkStateStore = app.checkState.ctx.KVStore(capKey)
checkStateStore = app.checkState.ctx.KVStore(capKey1)
storedBytes := checkStateStore.Get(counterKey)
require.Nil(t, storedBytes)
}
@ -471,15 +478,19 @@ func TestCheckTx(t *testing.T) {
// Test that successive DeliverTx can see each others' effects
// on the store, both within and across blocks.
func TestDeliverTx(t *testing.T) {
app, capKey, _ := setupBaseApp(t)
// test increments in the ante
anteKey := []byte("ante-key")
app.SetAnteHandler(anteHandlerTxTest(t, capKey, anteKey))
anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) }
// test increments in the handler
deliverKey := []byte("deliver-key")
app.Router().AddRoute(typeMsgCounter, handlerMsgCounter(t, capKey, deliverKey))
routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(typeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) }
app := setupBaseApp(t, anteOpt, routerOpt)
// Create same codec used in txDecoder
codec := wire.NewCodec()
registerTestCodec(codec)
nBlocks := 3
txPerHeight := 5
@ -488,7 +499,7 @@ func TestDeliverTx(t *testing.T) {
for i := 0; i < txPerHeight; i++ {
counter := int64(blockN*txPerHeight + i)
tx := newTxCounter(counter, counter)
txBytes, err := app.cdc.MarshalBinary(tx)
txBytes, err := codec.MarshalBinary(tx)
require.NoError(t, err)
res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
@ -506,29 +517,35 @@ func TestMultiMsgCheckTx(t *testing.T) {
// One call to DeliverTx should process all the messages, in order.
func TestMultiMsgDeliverTx(t *testing.T) {
app, capKey, _ := setupBaseApp(t)
// increment the tx counter
anteKey := []byte("ante-key")
app.SetAnteHandler(anteHandlerTxTest(t, capKey, anteKey))
anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) }
// increment the msg counter
deliverKey := []byte("deliver-key")
deliverKey2 := []byte("deliver-key2")
app.Router().AddRoute(typeMsgCounter, handlerMsgCounter(t, capKey, deliverKey))
app.Router().AddRoute(typeMsgCounter2, handlerMsgCounter(t, capKey, deliverKey2))
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(typeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey))
bapp.Router().AddRoute(typeMsgCounter2, handlerMsgCounter(t, capKey1, deliverKey2))
}
app := setupBaseApp(t, anteOpt, routerOpt)
// Create same codec used in txDecoder
codec := wire.NewCodec()
registerTestCodec(codec)
// run a multi-msg tx
// with all msgs the same type
{
app.BeginBlock(abci.RequestBeginBlock{})
tx := newTxCounter(0, 0, 1, 2)
txBytes, err := app.cdc.MarshalBinary(tx)
txBytes, err := codec.MarshalBinary(tx)
require.NoError(t, err)
res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
store := app.deliverState.ctx.KVStore(capKey)
store := app.deliverState.ctx.KVStore(capKey1)
// tx counter only incremented once
txCounter := getIntFromStore(store, anteKey)
@ -544,12 +561,12 @@ func TestMultiMsgDeliverTx(t *testing.T) {
tx := newTxCounter(1, 3)
tx.Msgs = append(tx.Msgs, msgCounter2{0})
tx.Msgs = append(tx.Msgs, msgCounter2{1})
txBytes, err := app.cdc.MarshalBinary(tx)
txBytes, err := codec.MarshalBinary(tx)
require.NoError(t, err)
res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
store := app.deliverState.ctx.KVStore(capKey)
store := app.deliverState.ctx.KVStore(capKey1)
// tx counter only incremented once
txCounter := getIntFromStore(store, anteKey)
@ -575,20 +592,30 @@ func TestConcurrentCheckDeliver(t *testing.T) {
// Simulate() and Query("/app/simulate", txBytes) should give
// the same results.
func TestSimulateTx(t *testing.T) {
app, _, _ := setupBaseApp(t)
gasConsumed := int64(5)
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasConsumed))
return
})
app.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx.GasMeter().ConsumeGas(gasConsumed, "test")
return sdk.Result{GasUsed: ctx.GasMeter().GasConsumed()}
})
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasConsumed))
return
})
}
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx.GasMeter().ConsumeGas(gasConsumed, "test")
return sdk.Result{GasUsed: ctx.GasMeter().GasConsumed()}
})
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.InitChain(abci.RequestInitChain{})
// Create same codec used in txDecoder
codec := wire.NewCodec()
registerTestCodec(codec)
nBlocks := 3
for blockN := 0; blockN < nBlocks; blockN++ {
count := int64(blockN + 1)
@ -607,7 +634,7 @@ func TestSimulateTx(t *testing.T) {
require.Equal(t, int64(gasConsumed), result.GasUsed)
// simulate by calling Query with encoded tx
txBytes, err := app.cdc.MarshalBinary(tx)
txBytes, err := codec.MarshalBinary(tx)
require.Nil(t, err)
query := abci.RequestQuery{
Path: "/app/simulate",
@ -617,7 +644,8 @@ func TestSimulateTx(t *testing.T) {
require.True(t, queryResult.IsOK(), queryResult.Log)
var res sdk.Result
app.cdc.MustUnmarshalBinary(queryResult.Value, &res)
wire.Cdc.MustUnmarshalBinary(queryResult.Value, &res)
require.Nil(t, err, "Result unmarshalling failed")
require.True(t, res.IsOK(), res.Log)
require.Equal(t, gasConsumed, res.GasUsed, res.Log)
app.EndBlock(abci.RequestEndBlock{})
@ -630,10 +658,14 @@ func TestSimulateTx(t *testing.T) {
// TODO: add more
func TestRunInvalidTransaction(t *testing.T) {
app, _, _ := setupBaseApp(t)
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
}
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.BeginBlock(abci.RequestBeginBlock{})
// Transaction with no messages
@ -700,43 +732,49 @@ func TestRunInvalidTransaction(t *testing.T) {
// Test that transactions exceeding gas limits fail
func TestTxGasLimits(t *testing.T) {
app, _, _ := setupBaseApp(t)
gasGranted := int64(10)
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
// NOTE/TODO/XXX:
// AnteHandlers must have their own defer/recover in order
// for the BaseApp to know how much gas was used used!
// This is because the GasMeter is created in the AnteHandler,
// but if it panics the context won't be set properly in runTx's recover ...
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasGranted
res.GasUsed = newCtx.GasMeter().GasConsumed()
default:
panic(r)
// NOTE/TODO/XXX:
// AnteHandlers must have their own defer/recover in order
// for the BaseApp to know how much gas was used used!
// This is because the GasMeter is created in the AnteHandler,
// but if it panics the context won't be set properly in runTx's recover ...
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasGranted
res.GasUsed = newCtx.GasMeter().GasConsumed()
default:
panic(r)
}
}
}
}()
}()
count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(count, "counter-ante")
res = sdk.Result{
GasWanted: gasGranted,
}
return
})
app.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
count := msg.(msgCounter).Counter
ctx.GasMeter().ConsumeGas(count, "counter-handler")
return sdk.Result{}
})
count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(count, "counter-ante")
res = sdk.Result{
GasWanted: gasGranted,
}
return
})
}
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
count := msg.(msgCounter).Counter
ctx.GasMeter().ConsumeGas(count, "counter-handler")
return sdk.Result{}
})
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.BeginBlock(abci.RequestBeginBlock{})
@ -785,20 +823,25 @@ func TestTxGasLimits(t *testing.T) {
// Test that we can only query from the latest committed state.
func TestQuery(t *testing.T) {
app, capKey, _ := setupBaseApp(t)
key, value := []byte("hello"), []byte("goodbye")
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
store := ctx.KVStore(capKey)
store.Set(key, value)
return
})
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
store := ctx.KVStore(capKey1)
store.Set(key, value)
return
})
}
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(capKey1)
store.Set(key, value)
return sdk.Result{}
})
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(capKey)
store.Set(key, value)
return sdk.Result{}
})
app.InitChain(abci.RequestInitChain{})
// NOTE: "/store/key1" tells us KVStore
@ -835,17 +878,21 @@ func TestQuery(t *testing.T) {
// Test p2p filter queries
func TestP2PQuery(t *testing.T) {
app, _, _ := setupBaseApp(t)
addrPeerFilterOpt := func(bapp *BaseApp) {
bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
require.Equal(t, "1.1.1.1:8000", addrport)
return abci.ResponseQuery{Code: uint32(3)}
})
}
app.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
require.Equal(t, "1.1.1.1:8000", addrport)
return abci.ResponseQuery{Code: uint32(3)}
})
pubkeyPeerFilterOpt := func(bapp *BaseApp) {
bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
require.Equal(t, "testpubkey", pubkey)
return abci.ResponseQuery{Code: uint32(4)}
})
}
app.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
require.Equal(t, "testpubkey", pubkey)
return abci.ResponseQuery{Code: uint32(4)}
})
app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt)
addrQuery := abci.RequestQuery{
Path: "/p2p/filter/addr/1.1.1.1:8000",

View File

@ -31,12 +31,12 @@ func NewRouter() *router {
}
}
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString
// AddRoute - TODO add description
func (rtr *router) AddRoute(r string, h sdk.Handler) Router {
if !isAlpha(r) {
panic("route expressions can only contain alphabet characters")
if !isAlphaNumeric(r) {
panic("route expressions can only contain alphanumeric characters")
}
rtr.routes = append(rtr.routes, route{r, h})

83
baseapp/setters.go Normal file
View File

@ -0,0 +1,83 @@
package baseapp
import (
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// nolint - Setter functions
func (app *BaseApp) SetName(name string) {
if app.sealed {
panic("SetName() on sealed BaseApp")
}
app.name = name
}
func (app *BaseApp) SetDB(db dbm.DB) {
if app.sealed {
panic("SetDB() on sealed BaseApp")
}
app.db = db
}
func (app *BaseApp) SetCMS(cms store.CommitMultiStore) {
if app.sealed {
panic("SetEndBlocker() on sealed BaseApp")
}
app.cms = cms
}
func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
if app.sealed {
panic("SetTxDecoder() on sealed BaseApp")
}
app.txDecoder = txDecoder
}
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
if app.sealed {
panic("SetInitChainer() on sealed BaseApp")
}
app.initChainer = initChainer
}
func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) {
if app.sealed {
panic("SetBeginBlocker() on sealed BaseApp")
}
app.beginBlocker = beginBlocker
}
func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
if app.sealed {
panic("SetEndBlocker() on sealed BaseApp")
}
app.endBlocker = endBlocker
}
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
if app.sealed {
panic("SetAnteHandler() on sealed BaseApp")
}
app.anteHandler = ah
}
func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) {
if app.sealed {
panic("SetAddrPeerFilter() on sealed BaseApp")
}
app.addrPeerFilter = pf
}
func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) {
if app.sealed {
panic("SetPubKeyPeerFilter() on sealed BaseApp")
}
app.pubkeyPeerFilter = pf
}
func (app *BaseApp) Router() Router {
if app.sealed {
panic("Router() on sealed BaseApp")
}
return app.router
}
func (app *BaseApp) Seal() { app.sealed = true }
func (app *BaseApp) IsSealed() bool { return app.sealed }
func (app *BaseApp) enforceSeal() {
if !app.sealed {
panic("enforceSeal() on BaseApp but not sealed")
}
}

115
client/context/context.go Normal file
View File

@ -0,0 +1,115 @@
package context
import (
"io"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/spf13/viper"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
const ctxAccStoreName = "acc"
// CLIContext implements a typical CLI context created in SDK modules for
// transaction handling and queries.
type CLIContext struct {
Codec *wire.Codec
AccDecoder auth.AccountDecoder
Client rpcclient.Client
Logger io.Writer
Height int64
NodeURI string
FromAddressName string
AccountStore string
TrustNode bool
UseLedger bool
Async bool
JSON bool
PrintResponse bool
}
// NewCLIContext returns a new initialized CLIContext with parameters from the
// command line using Viper.
func NewCLIContext() CLIContext {
var rpc rpcclient.Client
nodeURI := viper.GetString(client.FlagNode)
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
return CLIContext{
Client: rpc,
NodeURI: nodeURI,
AccountStore: ctxAccStoreName,
FromAddressName: viper.GetString(client.FlagFrom),
Height: viper.GetInt64(client.FlagHeight),
TrustNode: viper.GetBool(client.FlagTrustNode),
UseLedger: viper.GetBool(client.FlagUseLedger),
Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
}
}
// WithCodec returns a copy of the context with an updated codec.
func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext {
ctx.Codec = cdc
return ctx
}
// WithAccountDecoder returns a copy of the context with an updated account
// decoder.
func (ctx CLIContext) WithAccountDecoder(decoder auth.AccountDecoder) CLIContext {
ctx.AccDecoder = decoder
return ctx
}
// WithLogger returns a copy of the context with an updated logger.
func (ctx CLIContext) WithLogger(w io.Writer) CLIContext {
ctx.Logger = w
return ctx
}
// WithAccountStore returns a copy of the context with an updated AccountStore.
func (ctx CLIContext) WithAccountStore(accountStore string) CLIContext {
ctx.AccountStore = accountStore
return ctx
}
// WithFromAddressName returns a copy of the context with an updated from
// address.
func (ctx CLIContext) WithFromAddressName(addrName string) CLIContext {
ctx.FromAddressName = addrName
return ctx
}
// WithTrustNode returns a copy of the context with an updated TrustNode flag.
func (ctx CLIContext) WithTrustNode(trustNode bool) CLIContext {
ctx.TrustNode = trustNode
return ctx
}
// WithNodeURI returns a copy of the context with an updated node URI.
func (ctx CLIContext) WithNodeURI(nodeURI string) CLIContext {
ctx.NodeURI = nodeURI
ctx.Client = rpcclient.NewHTTP(nodeURI, "/websocket")
return ctx
}
// WithClient returns a copy of the context with an updated RPC client
// instance.
func (ctx CLIContext) WithClient(client rpcclient.Client) CLIContext {
ctx.Client = client
return ctx
}
// WithUseLedger returns a copy of the context with an updated UseLedger flag.
func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
ctx.UseLedger = useLedger
return ctx
}

13
client/context/errors.go Normal file
View File

@ -0,0 +1,13 @@
package context
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
)
// ErrInvalidAccount returns a standardized error reflecting that a given
// account address does not exist.
func ErrInvalidAccount(addr sdk.AccAddress) error {
return errors.Errorf(`No account with address %s was found in the state.
Are you sure there has been a transaction involving it?`, addr)
}

View File

@ -1,347 +0,0 @@
package context
import (
"fmt"
"github.com/tendermint/tendermint/libs/common"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Broadcast the transaction bytes to Tendermint
func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxCommit(tx)
if err != nil {
return res, err
}
if res.CheckTx.Code != uint32(0) {
return res, errors.Errorf("checkTx failed: (%d) %s",
res.CheckTx.Code,
res.CheckTx.Log)
}
if res.DeliverTx.Code != uint32(0) {
return res, errors.Errorf("deliverTx failed: (%d) %s",
res.DeliverTx.Code,
res.DeliverTx.Log)
}
return res, err
}
// Broadcast the transaction bytes to Tendermint
func (ctx CoreContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxAsync(tx)
if err != nil {
return res, err
}
return res, err
}
// Query information about the connected node
func (ctx CoreContext) Query(path string) (res []byte, err error) {
return ctx.query(path, nil)
}
// QueryStore from Tendermint with the provided key and storename
func (ctx CoreContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) {
return ctx.queryStore(key, storeName, "key")
}
// Query from Tendermint with the provided storename and subspace
func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName string) (res []sdk.KVPair, err error) {
resRaw, err := ctx.queryStore(subspace, storeName, "subspace")
if err != nil {
return res, err
}
cdc.MustUnmarshalBinary(resRaw, &res)
return
}
// Query from Tendermint with the provided storename and path
func (ctx CoreContext) query(path string, key common.HexBytes) (res []byte, err error) {
node, err := ctx.GetNode()
if err != nil {
return res, err
}
opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height,
Trusted: ctx.TrustNode,
}
result, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
return res, err
}
resp := result.Response
if resp.Code != uint32(0) {
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
}
return resp.Value, nil
}
// Query from Tendermint with the provided storename and path
func (ctx CoreContext) queryStore(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}
// Get the from address from the name flag
func (ctx CoreContext) GetFromAddress() (from sdk.AccAddress, err error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
name := ctx.FromAddressName
if name == "" {
return nil, errors.Errorf("must provide a from address name")
}
info, err := keybase.Get(name)
if err != nil {
return nil, errors.Errorf("no key for: %s", name)
}
return sdk.AccAddress(info.GetPubKey().Address()), nil
}
// sign and build the transaction from the msg
func (ctx CoreContext) SignAndBuild(name, passphrase string, msgs []sdk.Msg, cdc *wire.Codec) ([]byte, error) {
// build the Sign Messsage from the Standard Message
chainID := ctx.ChainID
if chainID == "" {
return nil, errors.Errorf("chain ID required but not specified")
}
accnum := ctx.AccountNumber
sequence := ctx.Sequence
memo := ctx.Memo
fee := sdk.Coin{}
if ctx.Fee != "" {
parsedFee, err := sdk.ParseCoin(ctx.Fee)
if err != nil {
return nil, err
}
fee = parsedFee
}
signMsg := auth.StdSignMsg{
ChainID: chainID,
AccountNumber: accnum,
Sequence: sequence,
Msgs: msgs,
Memo: memo,
Fee: auth.NewStdFee(ctx.Gas, fee), // TODO run simulate to estimate gas?
}
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
// sign and build
bz := signMsg.Bytes()
sig, pubkey, err := keybase.Sign(name, passphrase, bz)
if err != nil {
return nil, err
}
sigs := []auth.StdSignature{{
PubKey: pubkey,
Signature: sig,
AccountNumber: accnum,
Sequence: sequence,
}}
// marshal bytes
tx := auth.NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, memo)
return cdc.MarshalBinary(tx)
}
// sign and build the transaction from the msg
func (ctx CoreContext) ensureSignBuild(name string, msgs []sdk.Msg, cdc *wire.Codec) (tyBytes []byte, err error) {
err = EnsureAccountExists(ctx, name)
if err != nil {
return nil, err
}
ctx, err = EnsureAccountNumber(ctx)
if err != nil {
return nil, err
}
// default to next sequence number if none provided
ctx, err = EnsureSequence(ctx)
if err != nil {
return nil, err
}
var txBytes []byte
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
info, err := keybase.Get(name)
if err != nil {
return nil, err
}
var passphrase string
// Only need a passphrase for locally-stored keys
if info.GetType() == "local" {
passphrase, err = ctx.GetPassphraseFromStdin(name)
if err != nil {
return nil, fmt.Errorf("Error fetching passphrase: %v", err)
}
}
txBytes, err = ctx.SignAndBuild(name, passphrase, msgs, cdc)
if err != nil {
return nil, fmt.Errorf("Error signing transaction: %v", err)
}
return txBytes, err
}
// sign and build the transaction from the msg
func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msgs []sdk.Msg, cdc *wire.Codec) (err error) {
txBytes, err := ctx.ensureSignBuild(name, msgs, cdc)
if err != nil {
return err
}
if ctx.Async {
res, err := ctx.BroadcastTxAsync(txBytes)
if err != nil {
return err
}
if ctx.JSON {
type toJSON struct {
TxHash string
}
valueToJSON := toJSON{res.Hash.String()}
JSON, err := cdc.MarshalJSON(valueToJSON)
if err != nil {
return err
}
fmt.Println(string(JSON))
} else {
fmt.Println("Async tx sent. tx hash: ", res.Hash.String())
}
return nil
}
res, err := ctx.BroadcastTx(txBytes)
if err != nil {
return err
}
if ctx.JSON {
// Since JSON is intended for automated scripts, always include response in JSON mode
type toJSON struct {
Height int64
TxHash string
Response string
}
valueToJSON := toJSON{res.Height, res.Hash.String(), fmt.Sprintf("%+v", res.DeliverTx)}
JSON, err := cdc.MarshalJSON(valueToJSON)
if err != nil {
return err
}
fmt.Println(string(JSON))
return nil
}
if ctx.PrintResponse {
fmt.Printf("Committed at block %d. Hash: %s Response:%+v \n", res.Height, res.Hash.String(), res.DeliverTx)
} else {
fmt.Printf("Committed at block %d. Hash: %s \n", res.Height, res.Hash.String())
}
return nil
}
// get the next sequence for the account address
func (ctx CoreContext) GetAccountNumber(address []byte) (int64, error) {
if ctx.Decoder == nil {
return 0, errors.New("accountDecoder required but not provided")
}
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
if err != nil {
return 0, err
}
if len(res) == 0 {
fmt.Printf("No account found. Returning 0.\n")
return 0, err
}
account, err := ctx.Decoder(res)
if err != nil {
panic(err)
}
return account.GetAccountNumber(), nil
}
// get the next sequence for the account address
func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
if ctx.Decoder == nil {
return 0, errors.New("accountDecoder required but not provided")
}
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
if err != nil {
return 0, err
}
if len(res) == 0 {
fmt.Printf("No account found, defaulting to sequence 0\n")
return 0, err
}
account, err := ctx.Decoder(res)
if err != nil {
panic(err)
}
return account.GetSequence(), nil
}
// get passphrase from std input
func (ctx CoreContext) GetPassphraseFromStdin(name string) (pass string, err error) {
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password to sign with '%s':", name)
return client.GetPassword(prompt, buf)
}
// GetNode prepares a simple rpc.Client
func (ctx CoreContext) GetNode() (rpcclient.Client, error) {
if ctx.Client == nil {
return nil, errors.New("must define node URI")
}
return ctx.Client, nil
}

311
client/context/query.go Normal file
View File

@ -0,0 +1,311 @@
package context
import (
"fmt"
"io"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/libs/common"
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
// GetNode returns an RPC client. If the context's client is not defined, an
// error is returned.
func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
if ctx.Client == nil {
return nil, errors.New("no RPC client defined")
}
return ctx.Client, nil
}
// Query performs a query for information about the connected node.
func (ctx CLIContext) Query(path string) (res []byte, err error) {
return ctx.query(path, nil)
}
// 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) {
return ctx.queryStore(key, storeName, "key")
}
// QuerySubspace performs a query from a Tendermint node with the provided
// store name and subspace.
func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sdk.KVPair, err error) {
resRaw, err := ctx.queryStore(subspace, storeName, "subspace")
if err != nil {
return res, err
}
ctx.Codec.MustUnmarshalBinary(resRaw, &res)
return
}
// GetAccount queries for an account given an address and a block height. An
// error is returned if the query or decoding fails.
func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) {
if ctx.AccDecoder == nil {
return nil, errors.New("account decoder required but not provided")
}
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
if err != nil {
return nil, err
} else if len(res) == 0 {
return nil, err
}
account, err := ctx.AccDecoder(res)
if err != nil {
return nil, err
}
return account, nil
}
// GetFromAddress returns the from address from the context's name.
func (ctx CLIContext) GetFromAddress() (from sdk.AccAddress, err error) {
if ctx.FromAddressName == "" {
return nil, errors.Errorf("must provide a from address name")
}
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
info, err := keybase.Get(ctx.FromAddressName)
if err != nil {
return nil, errors.Errorf("no key for: %s", ctx.FromAddressName)
}
return sdk.AccAddress(info.GetPubKey().Address()), nil
}
// GetAccountNumber returns the next account number for the given account
// address.
func (ctx CLIContext) GetAccountNumber(address []byte) (int64, error) {
account, err := ctx.GetAccount(address)
if err != nil {
return 0, err
}
return account.GetAccountNumber(), nil
}
// GetAccountSequence returns the sequence number for the given account
// address.
func (ctx CLIContext) GetAccountSequence(address []byte) (int64, error) {
account, err := ctx.GetAccount(address)
if err != nil {
return 0, err
}
return account.GetSequence(), nil
}
// BroadcastTx broadcasts transaction bytes to a Tendermint node.
func (ctx CLIContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxCommit(tx)
if err != nil {
return res, err
}
if !res.CheckTx.IsOK() {
return res, errors.Errorf("checkTx failed: (%d) %s",
res.CheckTx.Code,
res.CheckTx.Log)
}
if !res.DeliverTx.IsOK() {
return res, errors.Errorf("deliverTx failed: (%d) %s",
res.DeliverTx.Code,
res.DeliverTx.Log)
}
return res, err
}
// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node
// asynchronously.
func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxAsync(tx)
if err != nil {
return res, err
}
return res, err
}
// EnsureAccountExists ensures that an account exists for a given context. An
// error is returned if it does not.
func (ctx CLIContext) EnsureAccountExists() error {
addr, err := ctx.GetFromAddress()
if err != nil {
return err
}
accountBytes, err := ctx.QueryStore(auth.AddressStoreKey(addr), ctx.AccountStore)
if err != nil {
return err
}
if len(accountBytes) == 0 {
return ErrInvalidAccount(addr)
}
return nil
}
// EnsureAccountExistsFromAddr ensures that an account exists for a given
// address. Instead of using the context's from name, a direct address is
// given. An error is returned if it does not.
func (ctx CLIContext) EnsureAccountExistsFromAddr(addr sdk.AccAddress) error {
accountBytes, err := ctx.QueryStore(auth.AddressStoreKey(addr), ctx.AccountStore)
if err != nil {
return err
}
if len(accountBytes) == 0 {
return ErrInvalidAccount(addr)
}
return nil
}
// EnsureBroadcastTx broadcasts a transactions either synchronously or
// asynchronously based on the context parameters. The result of the broadcast
// is parsed into an intermediate structure which is logged if the context has
// a logger defined.
func (ctx CLIContext) EnsureBroadcastTx(txBytes []byte) error {
if ctx.Async {
return ctx.ensureBroadcastTxAsync(txBytes)
}
return ctx.ensureBroadcastTx(txBytes)
}
func (ctx CLIContext) ensureBroadcastTxAsync(txBytes []byte) error {
res, err := ctx.BroadcastTxAsync(txBytes)
if err != nil {
return err
}
if ctx.JSON {
type toJSON struct {
TxHash string
}
if ctx.Logger != nil {
resJSON := toJSON{res.Hash.String()}
bz, err := ctx.Codec.MarshalJSON(resJSON)
if err != nil {
return err
}
ctx.Logger.Write(bz)
io.WriteString(ctx.Logger, "\n")
}
} else {
if ctx.Logger != nil {
io.WriteString(ctx.Logger, fmt.Sprintf("Async tx sent (tx hash: %s)\n", res.Hash))
}
}
return nil
}
func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error {
res, err := ctx.BroadcastTx(txBytes)
if err != nil {
return err
}
if ctx.JSON {
// since JSON is intended for automated scripts, always include
// response in JSON mode.
type toJSON struct {
Height int64
TxHash string
Response string
}
if ctx.Logger != nil {
resJSON := toJSON{res.Height, res.Hash.String(), fmt.Sprintf("%+v", res.DeliverTx)}
bz, err := ctx.Codec.MarshalJSON(resJSON)
if err != nil {
return err
}
ctx.Logger.Write(bz)
io.WriteString(ctx.Logger, "\n")
}
return nil
}
if ctx.Logger != nil {
resStr := fmt.Sprintf("Committed at block %d (tx hash: %s)\n", res.Height, res.Hash.String())
if ctx.PrintResponse {
resStr = fmt.Sprintf("Committed at block %d (tx hash: %s, response: %+v)\n",
res.Height, res.Hash.String(), res.DeliverTx,
)
}
io.WriteString(ctx.Logger, resStr)
}
return nil
}
// query performs a query from a Tendermint node with the provided store name
// and path.
func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err error) {
node, err := ctx.GetNode()
if err != nil {
return res, err
}
opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height,
Trusted: ctx.TrustNode,
}
result, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
return res, err
}
resp := result.Response
if !resp.IsOK() {
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
}
return resp.Value, nil
}
// queryStore performs a query from a Tendermint node with the provided a store
// name and path.
func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}

View File

@ -1,113 +0,0 @@
package context
import (
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// typical context created in sdk modules for transactions/queries
type CoreContext struct {
ChainID string
Height int64
Gas int64
Fee string
TrustNode bool
NodeURI string
FromAddressName string
AccountNumber int64
Sequence int64
Memo string
Client rpcclient.Client
Decoder auth.AccountDecoder
AccountStore string
UseLedger bool
Async bool
JSON bool
PrintResponse bool
}
// WithChainID - return a copy of the context with an updated chainID
func (c CoreContext) WithChainID(chainID string) CoreContext {
c.ChainID = chainID
return c
}
// WithHeight - return a copy of the context with an updated height
func (c CoreContext) WithHeight(height int64) CoreContext {
c.Height = height
return c
}
// WithGas - return a copy of the context with an updated gas
func (c CoreContext) WithGas(gas int64) CoreContext {
c.Gas = gas
return c
}
// WithFee - return a copy of the context with an updated fee
func (c CoreContext) WithFee(fee string) CoreContext {
c.Fee = fee
return c
}
// WithTrustNode - return a copy of the context with an updated TrustNode flag
func (c CoreContext) WithTrustNode(trustNode bool) CoreContext {
c.TrustNode = trustNode
return c
}
// WithNodeURI - return a copy of the context with an updated node URI
func (c CoreContext) WithNodeURI(nodeURI string) CoreContext {
c.NodeURI = nodeURI
c.Client = rpcclient.NewHTTP(nodeURI, "/websocket")
return c
}
// WithFromAddressName - return a copy of the context with an updated from address
func (c CoreContext) WithFromAddressName(fromAddressName string) CoreContext {
c.FromAddressName = fromAddressName
return c
}
// WithSequence - return a copy of the context with an account number
func (c CoreContext) WithAccountNumber(accnum int64) CoreContext {
c.AccountNumber = accnum
return c
}
// WithSequence - return a copy of the context with an updated sequence number
func (c CoreContext) WithSequence(sequence int64) CoreContext {
c.Sequence = sequence
return c
}
// WithMemo - return a copy of the context with an updated memo
func (c CoreContext) WithMemo(memo string) CoreContext {
c.Memo = memo
return c
}
// WithClient - return a copy of the context with an updated RPC client instance
func (c CoreContext) WithClient(client rpcclient.Client) CoreContext {
c.Client = client
return c
}
// WithDecoder - return a copy of the context with an updated Decoder
func (c CoreContext) WithDecoder(decoder auth.AccountDecoder) CoreContext {
c.Decoder = decoder
return c
}
// WithAccountStore - return a copy of the context with an updated AccountStore
func (c CoreContext) WithAccountStore(accountStore string) CoreContext {
c.AccountStore = accountStore
return c
}
// WithUseLedger - return a copy of the context with an updated UseLedger
func (c CoreContext) WithUseLedger(useLedger bool) CoreContext {
c.UseLedger = useLedger
return c
}

View File

@ -1,141 +0,0 @@
package context
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/viper"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
rpcclient "github.com/tendermint/tendermint/rpc/client"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// NewCoreContextFromViper - return a new context with parameters from the command line
func NewCoreContextFromViper() CoreContext {
nodeURI := viper.GetString(client.FlagNode)
var rpc rpcclient.Client
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
chainID := viper.GetString(client.FlagChainID)
// if chain ID is not specified manually, read default chain ID
if chainID == "" {
def, err := defaultChainID()
if err != nil {
chainID = def
}
}
// TODO: Remove the following deprecation code after Gaia-7000 is launched
keyName := viper.GetString(client.FlagName)
if keyName != "" {
fmt.Println("** Note --name is deprecated and will be removed next release. Please use --from instead **")
} else {
keyName = viper.GetString(client.FlagFrom)
}
return CoreContext{
ChainID: chainID,
Height: viper.GetInt64(client.FlagHeight),
Gas: viper.GetInt64(client.FlagGas),
Fee: viper.GetString(client.FlagFee),
TrustNode: viper.GetBool(client.FlagTrustNode),
FromAddressName: keyName,
NodeURI: nodeURI,
AccountNumber: viper.GetInt64(client.FlagAccountNumber),
Sequence: viper.GetInt64(client.FlagSequence),
Memo: viper.GetString(client.FlagMemo),
Client: rpc,
Decoder: nil,
AccountStore: "acc",
UseLedger: viper.GetBool(client.FlagUseLedger),
Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
}
}
// read chain ID from genesis file, if present
func defaultChainID() (string, error) {
cfg, err := tcmd.ParseConfig()
if err != nil {
return "", err
}
doc, err := tmtypes.GenesisDocFromFile(cfg.GenesisFile())
if err != nil {
return "", err
}
return doc.ChainID, nil
}
// EnsureAccountExists - Make sure account exists
func EnsureAccountExists(ctx CoreContext, name string) error {
keybase, err := keys.GetKeyBase()
if err != nil {
return err
}
if name == "" {
return errors.Errorf("must provide a from address name")
}
info, err := keybase.Get(name)
if err != nil {
return errors.Errorf("no key for: %s", name)
}
accAddr := sdk.AccAddress(info.GetPubKey().Address())
Acc, err := ctx.QueryStore(auth.AddressStoreKey(accAddr), ctx.AccountStore)
if err != nil {
return err
}
// Check if account was found
if Acc == nil {
return errors.Errorf("No account with address %s was found in the state.\nAre you sure there has been a transaction involving it?", accAddr)
}
return nil
}
// EnsureAccount - automatically set account number if none provided
func EnsureAccountNumber(ctx CoreContext) (CoreContext, error) {
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
if viper.GetInt64(client.FlagAccountNumber) != 0 {
return ctx, nil
}
from, err := ctx.GetFromAddress()
if err != nil {
return ctx, err
}
accnum, err := ctx.GetAccountNumber(from)
if err != nil {
return ctx, err
}
fmt.Printf("Defaulting to account number: %d\n", accnum)
ctx = ctx.WithAccountNumber(accnum)
return ctx, nil
}
// EnsureSequence - automatically set sequence number if none provided
func EnsureSequence(ctx CoreContext) (CoreContext, error) {
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
if viper.GetInt64(client.FlagSequence) != 0 {
return ctx, nil
}
from, err := ctx.GetFromAddress()
if err != nil {
return ctx, err
}
seq, err := ctx.NextSequence(from)
if err != nil {
return ctx, err
}
fmt.Printf("Defaulting to next sequence number: %d\n", seq)
ctx = ctx.WithSequence(seq)
return ctx, nil
}

View File

@ -42,7 +42,6 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
for _, c := range cmds {
c.Flags().String(FlagFrom, "", "Name of private key with which to sign")
c.Flags().String(FlagName, "", "DEPRECATED - Name of private key with which to sign")
c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx")
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
c.Flags().String(FlagMemo, "", "Memo to send along with transaction")

View File

@ -28,12 +28,17 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) {
} else {
pass, err = readLineFromBuf(buf)
}
if err != nil {
return "", err
}
if len(pass) < MinPassLength {
return "", errors.Errorf("password must be at least %d characters", MinPassLength)
// Return the given password to the upstream client so it can handle a
// non-STDIN failure gracefully.
return pass, errors.Errorf("password must be at least %d characters", MinPassLength)
}
return pass, nil
}

View File

@ -29,6 +29,55 @@ func GetKeyBase() (keys.Keybase, error) {
return GetKeyBaseFromDir(rootDir)
}
// GetKeyInfo returns key info for a given name. An error is returned if the
// keybase cannot be retrieved or getting the info fails.
func GetKeyInfo(name string) (keys.Info, error) {
keybase, err := GetKeyBase()
if err != nil {
return nil, err
}
return keybase.Get(name)
}
// GetPassphrase returns a passphrase for a given name. It will first retrieve
// the key info for that name if the type is local, it'll fetch input from
// STDIN. Otherwise, an empty passphrase is returned. An error is returned if
// the key info cannot be fetched or reading from STDIN fails.
func GetPassphrase(name string) (string, error) {
var passphrase string
keyInfo, err := GetKeyInfo(name)
if err != nil {
return passphrase, err
}
// we only need a passphrase for locally stored keys
// TODO: (ref: #864) address security concerns
if keyInfo.GetType() == keys.TypeLocal {
passphrase, err = ReadPassphraseFromStdin(name)
if err != nil {
return passphrase, err
}
}
return passphrase, nil
}
// ReadPassphraseFromStdin attempts to read a passphrase from STDIN return an
// error upon failure.
func ReadPassphraseFromStdin(name string) (string, error) {
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password to sign with '%s':", name)
passphrase, err := client.GetPassword(prompt, buf)
if err != nil {
return passphrase, fmt.Errorf("Error reading passphrase: %v", err)
}
return passphrase, nil
}
// initialize a keybase based on the configuration
func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
if keybase == nil {
@ -77,7 +126,7 @@ func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
}
return KeyOutput{
Name: info.GetName(),
Type: info.GetType(),
Type: info.GetType().String(),
Address: account,
PubKey: bechPubKey,
}, nil

1131
client/lcd/lcd_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -78,21 +78,21 @@ func createHandler(cdc *wire.Codec) http.Handler {
panic(err)
}
ctx := context.NewCoreContextFromViper()
cliCtx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout)
// TODO: make more functional? aka r = keys.RegisterRoutes(r)
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
r.HandleFunc("/node_version", NodeVersionRequestHandler(ctx)).Methods("GET")
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
keys.RegisterRoutes(r)
rpc.RegisterRoutes(ctx, r)
tx.RegisterRoutes(ctx, r, cdc)
auth.RegisterRoutes(ctx, r, cdc, "acc")
bank.RegisterRoutes(ctx, r, cdc, kb)
ibc.RegisterRoutes(ctx, r, cdc, kb)
stake.RegisterRoutes(ctx, r, cdc, kb)
slashing.RegisterRoutes(ctx, r, cdc, kb)
gov.RegisterRoutes(ctx, r, cdc)
rpc.RegisterRoutes(cliCtx, r)
tx.RegisterRoutes(cliCtx, r, cdc)
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
bank.RegisterRoutes(cliCtx, r, cdc, kb)
ibc.RegisterRoutes(cliCtx, r, cdc, kb)
stake.RegisterRoutes(cliCtx, r, cdc, kb)
slashing.RegisterRoutes(cliCtx, r, cdc, kb)
gov.RegisterRoutes(cliCtx, r, cdc)
return r
}

View File

@ -12,10 +12,19 @@ import (
"strings"
"testing"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
abci "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
@ -28,29 +37,21 @@ import (
"github.com/tendermint/tendermint/proxy"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// f**ing long, but unique for each test
// makePathname creates a unique pathname for each test. It will panic if it
// cannot get the current working directory.
func makePathname() string {
// get path
p, err := os.Getwd()
if err != nil {
panic(err)
}
sep := string(filepath.Separator)
return strings.Replace(p, sep, "_", -1)
}
// GetConfig returns a config for the test cases as a singleton
// GetConfig returns a Tendermint config for the test cases.
func GetConfig() *tmcfg.Config {
pathname := makePathname()
config := tmcfg.ResetTestRoot(pathname)
@ -59,52 +60,73 @@ func GetConfig() *tmcfg.Config {
if err != nil {
panic(err)
}
rcpAddr, _, err := server.FreeTCPAddr()
if err != nil {
panic(err)
}
grpcAddr, _, err := server.FreeTCPAddr()
if err != nil {
panic(err)
}
config.P2P.ListenAddress = tmAddr
config.RPC.ListenAddress = rcpAddr
config.RPC.GRPCListenAddress = grpcAddr
return config
}
// get the lcd test keybase
// note can't use a memdb because the request is expecting to interact with the default location
func GetKB(t *testing.T) crkeys.Keybase {
// GetKeyBase returns the LCD test keybase. It also requires that a directory
// could be made and a keybase could be fetched.
//
// NOTE: memDB cannot be used because the request is expecting to interact with
// the default location.
func GetKeyBase(t *testing.T) crkeys.Keybase {
dir, err := ioutil.TempDir("", "lcd_test")
require.NoError(t, err)
viper.Set(cli.HomeFlag, dir)
keybase, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
keybase, err := keys.GetKeyBase()
require.NoError(t, err)
return keybase
}
// add an address to the store return name and password
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (addr sdk.AccAddress, seed string) {
var info crkeys.Info
var err error
// CreateAddr adds an address to the key store and returns an address and seed.
// It also requires that the key could be created.
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) {
var (
err error
info crkeys.Info
seed string
)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
require.NoError(t, err)
addr = sdk.AccAddress(info.GetPubKey().Address())
return
return sdk.AccAddress(info.GetPubKey().Address()), seed
}
// strt TM and the LCD in process, listening on their respective sockets
// nValidators = number of validators
// initAddrs = accounts to initialize with some steaks
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (cleanup func(), validatorsPKs []crypto.PubKey, port string) {
// InitializeTestLCD starts Tendermint and the LCD in process, listening on
// their respective sockets where nValidators is the total number of validators
// and initAddrs are the accounts to initialize with some steak tokens. It
// returns a cleanup function, a set of validator public keys, and a port.
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) {
config := GetConfig()
config.Consensus.TimeoutCommit = 100
config.Consensus.SkipTimeoutCommit = false
config.TxIndex.IndexAllTags = true
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
logger = log.NewFilter(logger, log.AllowDebug())
logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile()
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
privVal.Reset()
db := dbm.NewMemDB()
app := gapp.NewGaiaApp(logger, db, nil)
cdc = gapp.MakeCodec()
@ -113,10 +135,10 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
require.NoError(t, err)
// add more validators
if nValidators < 1 {
panic("InitializeTestLCD must use at least one validator")
}
for i := 1; i < nValidators; i++ {
genDoc.Validators = append(genDoc.Validators,
tmtypes.GenesisValidator{
@ -127,14 +149,18 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
)
}
// NOTE it's bad practice to reuse pk address for the owner address but doing in the
// test for simplicity
var validatorsPKs []crypto.PubKey
// NOTE: It's bad practice to reuse public key address for the owner
// address but doing in the test for simplicity.
var appGenTxs []json.RawMessage
for _, gdValidator := range genDoc.Validators {
pk := gdValidator.PubKey
validatorsPKs = append(validatorsPKs, pk) // append keys for output
validatorsPKs = append(validatorsPKs, pk)
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1")
require.NoError(t, err)
appGenTxs = append(appGenTxs, appGenTx)
}
@ -144,7 +170,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
// add some tokens to init accounts
for _, addr := range initAddrs {
accAuth := auth.NewBaseAccountWithAddress(addr)
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 100)}
acc := gapp.NewGenesisAccount(&accAuth)
genesisState.Accounts = append(genesisState.Accounts, acc)
genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100))
@ -154,79 +180,88 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
require.NoError(t, err)
genDoc.AppState = appState
// LCD listen address
var listenAddr string
listenAddr, port, err = server.FreeTCPAddr()
listenAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
// XXX: need to set this so LCD knows the tendermint node address!
// XXX: Need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress)
viper.Set(client.FlagChainID, genDoc.ChainID)
node, err := startTM(config, logger, genDoc, privVal, app)
require.NoError(t, err)
lcd, err := startLCD(logger, listenAddr, cdc)
require.NoError(t, err)
//time.Sleep(time.Second)
//tests.WaitForHeight(2, port)
tests.WaitForLCDStart(port)
tests.WaitForHeight(1, port)
// for use in defer
cleanup = func() {
cleanup := func() {
logger.Debug("cleaning up LCD initialization")
node.Stop()
node.Wait()
lcd.Close()
}
return
return cleanup, validatorsPKs, port
}
// Create & start in-process tendermint node with memdb
// and in-process abci application.
// TODO: need to clean up the WAL dir or enable it to be not persistent
func startTM(tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, privVal tmtypes.PrivValidator, app abci.Application) (*nm.Node, error) {
// startTM creates and starts an in-process Tendermint node with memDB and
// in-process ABCI application. It returns the new node or any error that
// occurred.
//
// TODO: Clean up the WAL dir or enable it to be not persistent!
func startTM(
tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc,
privVal tmtypes.PrivValidator, app abci.Application,
) (*nm.Node, error) {
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil }
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil }
n, err := nm.NewNode(tmcfg,
node, err := nm.NewNode(
tmcfg,
privVal,
proxy.NewLocalClientCreator(app),
genDocProvider,
dbProvider,
nm.DefaultMetricsProvider,
logger.With("module", "node"))
nm.DefaultMetricsProvider(tmcfg.Instrumentation),
logger.With("module", "node"),
)
if err != nil {
return nil, err
}
err = n.Start()
err = node.Start()
if err != nil {
return nil, err
}
// wait for rpc
tests.WaitForRPC(tmcfg.RPC.ListenAddress)
logger.Info("Tendermint running!")
return n, err
return node, err
}
// start the LCD. note this blocks!
// startLCD starts the LCD.
//
// NOTE: This causes the thread to block.
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
handler := createHandler(cdc)
return tmrpc.StartHTTPServer(listenAddr, handler, logger, tmrpc.Config{})
return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{})
}
// make a test lcd test request
// Request makes a test LCD test request. It returns a response object and a
// stringified response body.
func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
var res *http.Response
var err error
var (
err error
res *http.Response
)
url := fmt.Sprintf("http://localhost:%v%v", port, path)
fmt.Println("REQUEST " + method + " " + url)
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
require.Nil(t, err)
res, err = http.DefaultClient.Do(req)
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload))
require.Nil(t, err)
output, err := ioutil.ReadAll(res.Body)

View File

@ -15,14 +15,15 @@ func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) {
}
// connected node version REST handler endpoint
func NodeVersionRequestHandler(ctx context.CoreContext) http.HandlerFunc {
func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
version, err := ctx.Query("/app/version")
version, err := cliCtx.Query("/app/version")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error())))
return
}
w.Write(version)
}
}

View File

@ -5,11 +5,11 @@ import (
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
)
const (
@ -31,9 +31,9 @@ func BlockCommand() *cobra.Command {
return cmd
}
func getBlock(ctx context.CoreContext, height *int64) ([]byte, error) {
func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
// get the node
node, err := ctx.GetNode()
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
@ -57,8 +57,8 @@ func getBlock(ctx context.CoreContext, height *int64) ([]byte, error) {
}
// get the current blockchain height
func GetChainHeight(ctx context.CoreContext) (int64, error) {
node, err := ctx.GetNode()
func GetChainHeight(cliCtx context.CLIContext) (int64, error) {
node, err := cliCtx.GetNode()
if err != nil {
return -1, err
}
@ -86,7 +86,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
}
}
output, err := getBlock(context.NewCoreContextFromViper(), height)
output, err := getBlock(context.NewCLIContext(), height)
if err != nil {
return err
}
@ -97,7 +97,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
// REST
// REST handler to get a block
func BlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
@ -106,13 +106,13 @@ func BlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'."))
return
}
chainHeight, err := GetChainHeight(ctx)
chainHeight, err := GetChainHeight(cliCtx)
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := getBlock(ctx, &height)
output, err := getBlock(cliCtx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
@ -123,15 +123,15 @@ func BlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
}
// REST handler to get the latest block
func LatestBlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight(ctx)
height, err := GetChainHeight(cliCtx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
output, err := getBlock(ctx, &height)
output, err := getBlock(cliCtx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -44,11 +44,11 @@ func initClientCommand() *cobra.Command {
}
// Register REST endpoints
func RegisterRoutes(ctx context.CoreContext, r *mux.Router) {
r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(ctx)).Methods("GET")
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(cliCtx)).Methods("GET")
}

View File

@ -18,23 +18,25 @@ func statusCommand() *cobra.Command {
Short: "Query remote node for status",
RunE: printNodeStatus,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
return cmd
}
func getNodeStatus(ctx context.CoreContext) (*ctypes.ResultStatus, error) {
func getNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) {
// get the node
node, err := ctx.GetNode()
node, err := cliCtx.GetNode()
if err != nil {
return &ctypes.ResultStatus{}, err
}
return node.Status()
}
// CMD
func printNodeStatus(cmd *cobra.Command, args []string) error {
status, err := getNodeStatus(context.NewCoreContextFromViper())
status, err := getNodeStatus(context.NewCLIContext())
if err != nil {
return err
}
@ -52,9 +54,9 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
// REST
// REST handler for node info
func NodeInfoRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
status, err := getNodeStatus(cliCtx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
@ -68,14 +70,15 @@ func NodeInfoRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}
// REST handler for node syncing
func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func NodeSyncingRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
status, err := getNodeStatus(cliCtx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
@ -88,6 +91,7 @@ func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(strconv.FormatBool(syncing)))
}
}

View File

@ -58,9 +58,9 @@ func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error
}, nil
}
func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) {
// get the node
node, err := ctx.GetNode()
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
@ -74,6 +74,7 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
BlockHeight: validatorsRes.BlockHeight,
Validators: make([]ValidatorOutput, len(validatorsRes.Validators)),
}
for i := 0; i < len(validatorsRes.Validators); i++ {
outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i])
if err != nil {
@ -85,6 +86,7 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
if err != nil {
return nil, err
}
return output, nil
}
@ -104,7 +106,7 @@ func printValidators(cmd *cobra.Command, args []string) error {
}
}
output, err := getValidators(context.NewCoreContextFromViper(), height)
output, err := getValidators(context.NewCLIContext(), height)
if err != nil {
return err
}
@ -116,22 +118,25 @@ func printValidators(cmd *cobra.Command, args []string) error {
// REST
// Validator Set at a height REST handler
func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'."))
return
}
chainHeight, err := GetChainHeight(ctx)
chainHeight, err := GetChainHeight(cliCtx)
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := getValidators(ctx, &height)
output, err := getValidators(cliCtx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
@ -143,20 +148,22 @@ func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
}
// Latest Validator Set REST handler
func LatestValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight(ctx)
height, err := GetChainHeight(cliCtx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
output, err := getValidators(ctx, &height)
output, err := getValidators(cliCtx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -13,7 +13,7 @@ type BroadcastTxBody struct {
}
// BroadcastTx REST Handler
func BroadcastTxRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
func BroadcastTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m BroadcastTxBody
@ -25,7 +25,7 @@ func BroadcastTxRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return
}
res, err := ctx.BroadcastTx([]byte(m.TxBytes))
res, err := cliCtx.BroadcastTx([]byte(m.TxBytes))
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -21,24 +21,25 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
)
// Get the default command for a tx query
// QueryTxCmd implements the default command for a tx query.
func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "tx [hash]",
Short: "Matches this txhash over all committed blocks",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// find the key to look up the account
hashHexStr := args[0]
trustNode := viper.GetBool(client.FlagTrustNode)
output, err := queryTx(cdc, context.NewCoreContextFromViper(), hashHexStr, trustNode)
cliCtx := context.NewCLIContext().WithCodec(cdc)
output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
if err != nil {
return err
}
fmt.Println(string(output))
fmt.Println(string(output))
return nil
},
}
@ -50,14 +51,13 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
return cmd
}
func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustNode bool) ([]byte, error) {
func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) {
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return nil, err
}
// get the node
node, err := ctx.GetNode()
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
@ -66,6 +66,7 @@ func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustN
if err != nil {
return nil, err
}
info, err := formatTxResult(cdc, res)
if err != nil {
return nil, err
@ -74,24 +75,23 @@ func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustN
return wire.MarshalJSONIndent(cdc, info)
}
func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) {
func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (Info, error) {
// TODO: verify the proof if requested
tx, err := parseTx(cdc, res.Tx)
if err != nil {
return txInfo{}, err
return Info{}, err
}
info := txInfo{
return Info{
Hash: res.Hash,
Height: res.Height,
Tx: tx,
Result: res.TxResult,
}
return info, nil
}, nil
}
// txInfo is used to prepare info to display
type txInfo struct {
// Info is used to prepare info to display
type Info struct {
Hash common.HexBytes `json:"hash"`
Height int64 `json:"height"`
Tx sdk.Tx `json:"tx"`
@ -100,17 +100,19 @@ type txInfo struct {
func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
var tx auth.StdTx
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, err
}
return tx, nil
}
// REST
// transaction query REST handler
func QueryTxRequestHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
func QueryTxRequestHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
hashHexStr := vars["hash"]
@ -120,12 +122,13 @@ func QueryTxRequestHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.Hand
trustNode = true
}
output, err := queryTx(cdc, ctx, hashHexStr, trustNode)
output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -17,9 +17,9 @@ func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
}
// register REST routes
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, ctx)).Methods("GET")
r.HandleFunc("/txs", SearchTxRequestHandlerFn(ctx, cdc)).Methods("GET")
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) {
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
}

View File

@ -7,15 +7,15 @@ import (
"net/url"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/spf13/cobra"
"github.com/spf13/viper"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
const (
@ -31,14 +31,18 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
tags := viper.GetStringSlice(flagTags)
txs, err := searchTxs(context.NewCoreContextFromViper(), cdc, tags)
cliCtx := context.NewCLIContext().WithCodec(cdc)
txs, err := searchTxs(cliCtx, cdc, tags)
if err != nil {
return err
}
output, err := cdc.MarshalJSON(txs)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
},
@ -53,19 +57,22 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
return cmd
}
func searchTxs(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]txInfo, error) {
func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Info, error) {
if len(tags) == 0 {
return nil, errors.New("must declare at least one tag to search")
}
// XXX: implement ANY
query := strings.Join(tags, " AND ")
// get the node
node, err := ctx.GetNode()
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
prove := !viper.GetBool(client.FlagTrustNode)
// TODO: take these as args
page := 0
perPage := 100
@ -74,7 +81,7 @@ func searchTxs(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]txInf
return nil, err
}
info, err := formatTxResults(cdc, res.Txs)
info, err := FormatTxResults(cdc, res.Txs)
if err != nil {
return nil, err
}
@ -82,9 +89,10 @@ func searchTxs(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]txInf
return info, nil
}
func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error) {
// parse the indexed txs into an array of Info
func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) {
var err error
out := make([]txInfo, len(res))
out := make([]Info, len(res))
for i := range res {
out[i], err = formatTxResult(cdc, res[i])
if err != nil {
@ -98,7 +106,7 @@ func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error)
// REST
// Search Tx REST Handler
func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("tag")
if tag == "" {
@ -106,14 +114,17 @@ func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.Han
w.Write([]byte("You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys"))
return
}
keyValue := strings.Split(tag, "=")
key := keyValue[0]
value, err := url.QueryUnescape(keyValue[1])
if err != nil {
w.WriteHeader(400)
w.Write([]byte("Could not decode address: " + err.Error()))
return
}
if strings.HasSuffix(key, "_bech32") {
bech32address := strings.Trim(value, "'")
prefix := strings.Split(bech32address, "1")[0]
@ -127,7 +138,7 @@ func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.Han
tag = strings.TrimRight(key, "_bech32") + "='" + sdk.AccAddress(bz).String() + "'"
}
txs, err := searchTxs(ctx, cdc, []string{tag})
txs, err := searchTxs(cliCtx, cdc, []string{tag})
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -8,7 +8,7 @@ import (
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
)
// REST request body
// REST request body for signed txs
// TODO does this need to be exposed?
type SignTxBody struct {
Name string `json:"name"`
@ -44,5 +44,5 @@ func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.Write(sig.Bytes())
w.Write(sig)
}

60
client/utils/utils.go Normal file
View File

@ -0,0 +1,60 @@
package utils
import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context"
)
// SendTx implements a auxiliary handler that facilitates sending a series of
// messages in a signed transaction given a TxContext and a QueryContext. It
// ensures that the account exists, has a proper number and sequence set. In
// addition, it builds and signs a transaction with the supplied messages.
// Finally, it broadcasts the signed transaction to a node.
func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) error {
if err := cliCtx.EnsureAccountExists(); err != nil {
return err
}
from, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txCtx.AccountNumber == 0 {
accNum, err := cliCtx.GetAccountNumber(from)
if err != nil {
return err
}
txCtx = txCtx.WithAccountNumber(accNum)
}
// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txCtx.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return err
}
txCtx = txCtx.WithSequence(accSeq)
}
passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
if err != nil {
return err
}
// build and sign the transaction
txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
if err != nil {
return err
}
// broadcast to a Tendermint node
return cliCtx.EnsureBroadcastTx(txBytes)
}

View File

@ -0,0 +1,158 @@
package cmd
import (
"fmt"
"go/build"
"io/ioutil"
"os"
"strings"
"path/filepath"
"github.com/cosmos/cosmos-sdk/version"
"github.com/spf13/cobra"
tmversion "github.com/tendermint/tendermint/version"
)
var remoteBasecoinPath = "github.com/cosmos/cosmos-sdk/examples/basecoin"
// Replacer to replace all instances of basecoin/basecli/BasecoinApp to project specific names
// Gets initialized when initCmd is executing after getting the project name from user
var replacer *strings.Replacer
// Remote path for the project.
var remoteProjectPath string
func init() {
initCmd.Flags().StringVarP(&remoteProjectPath, "project-path", "p", "", "Remote project path. eg: github.com/your_user_name/project_name")
rootCmd.AddCommand(initCmd)
}
var initCmd = &cobra.Command{
Use: "init [ProjectName]",
Short: "Initialize your new cosmos zone",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Print("Thanks for choosing Cosmos-SDK to build your project.\n\n")
projectName := args[0]
capitalizedProjectName := strings.Title(projectName)
shortProjectName := strings.ToLower(projectName)
remoteProjectPath = strings.ToLower(strings.TrimSpace(remoteProjectPath))
if remoteProjectPath == "" {
remoteProjectPath = strings.ToLower(shortProjectName)
}
replacer = strings.NewReplacer("basecli", shortProjectName+"cli",
"basecoind", shortProjectName+"d",
"BasecoinApp", capitalizedProjectName+"App",
remoteBasecoinPath, remoteProjectPath,
"basecoin", shortProjectName,
"Basecoin", capitalizedProjectName)
return setupBasecoinWorkspace(shortProjectName, remoteProjectPath)
},
}
func resolveProjectPath(remoteProjectPath string) string {
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = build.Default.GOPATH
// Use $HOME/go
}
return gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator) + remoteProjectPath
}
func copyBasecoinTemplate(projectName string, projectPath string, remoteProjectPath string) {
basecoinProjectPath := resolveProjectPath(remoteBasecoinPath)
filepath.Walk(basecoinProjectPath, func(path string, f os.FileInfo, err error) error {
if !f.IsDir() {
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
contents := string(data)
// Extract relative file path eg: app/app.go instead of /Users/..../github.com/cosmos/...examples/basecoin/app/app.go
relativeFilePath := path[len(basecoinProjectPath)+1:]
// Evaluating the filepath in the new project folder
projectFilePath := projectPath + string(os.PathSeparator) + relativeFilePath
projectFilePath = replacer.Replace(projectFilePath)
lengthOfRootDir := strings.LastIndex(projectFilePath, string(os.PathSeparator))
// Extracting the path of root directory from the filepath
rootDir := projectFilePath[0:lengthOfRootDir]
// Creating the required directory first
os.MkdirAll(rootDir, os.ModePerm)
fmt.Println("Creating " + projectFilePath)
// Writing the contents to a file in the project folder
contents = replacer.Replace(contents)
ioutil.WriteFile(projectFilePath, []byte(contents), os.ModePerm)
}
return nil
})
}
func createGopkg(projectPath string) {
// Create gopkg.toml file
dependencies := map[string]string{
"github.com/cosmos/cosmos-sdk": "=" + version.Version,
"github.com/stretchr/testify": "=1.2.1",
"github.com/spf13/cobra": "=0.0.1",
"github.com/spf13/viper": "=1.0.0",
}
overrides := map[string]string{
"github.com/golang/protobuf": "1.1.0",
"github.com/tendermint/tendermint": tmversion.Version,
}
contents := ""
for dependency, version := range dependencies {
contents += "[[constraint]]\n\tname = \"" + dependency + "\"\n\tversion = \"" + version + "\"\n\n"
}
for dependency, version := range overrides {
contents += "[[override]]\n\tname = \"" + dependency + "\"\n\tversion = \"=" + version + "\"\n\n"
}
contents += "[prune]\n\tgo-tests = true\n\tunused-packages = true"
ioutil.WriteFile(projectPath+"/Gopkg.toml", []byte(contents), os.ModePerm)
}
func createMakefile(projectPath string) {
// Create makefile
// TODO: Should we use tools/ directory as in Cosmos-SDK to get tools for linting etc.
makefileContents := `PACKAGES=$(shell go list ./... | grep -v '/vendor/')
all: get_tools get_vendor_deps build test
get_tools:
go get github.com/golang/dep/cmd/dep
build:
go build -o bin/basecli cmd/basecli/main.go && go build -o bin/basecoind cmd/basecoind/main.go
get_vendor_deps:
@rm -rf vendor/
@dep ensure
test:
@go test $(PACKAGES)
benchmark:
@go test -bench=. $(PACKAGES)
.PHONY: all build test benchmark`
// Replacing instances of base* to project specific names
makefileContents = replacer.Replace(makefileContents)
ioutil.WriteFile(projectPath+"/Makefile", []byte(makefileContents), os.ModePerm)
}
func setupBasecoinWorkspace(projectName string, remoteProjectPath string) error {
projectPath := resolveProjectPath(remoteProjectPath)
fmt.Println("Configuring your project in " + projectPath)
// Check if the projectPath already exists or not
if _, err := os.Stat(projectPath); !os.IsNotExist(err) {
return fmt.Errorf("Unable to initialize the project. %s already exists", projectPath)
}
copyBasecoinTemplate(projectName, projectPath, remoteProjectPath)
createGopkg(projectPath)
createMakefile(projectPath)
fmt.Printf("Initialized a new project at %s.\nHappy hacking!\n", projectPath)
return nil
}

View File

@ -0,0 +1,21 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "cosmos-sdk-cli",
Short: "Tools to develop on cosmos-sdk",
}
// Execute the command
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -0,0 +1,9 @@
package main
import (
"github.com/cosmos/cosmos-sdk/cmd/cosmos-sdk-cli/cmd"
)
func main() {
cmd.Execute()
}

View File

@ -18,6 +18,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
)
@ -45,6 +46,8 @@ type GaiaApp struct {
keySlashing *sdk.KVStoreKey
keyGov *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
tkeyParams *sdk.TransientStoreKey
// Manage getting and setting accounts
accountMapper auth.AccountMapper
@ -54,13 +57,14 @@ type GaiaApp struct {
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
}
// NewGaiaApp returns a reference to an initialized GaiaApp.
func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
cdc := MakeCodec()
bApp := bam.NewBaseApp(appName, cdc, logger, db, baseAppOptions...)
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
var app = &GaiaApp{
@ -73,6 +77,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
keySlashing: sdk.NewKVStoreKey("slashing"),
keyGov: sdk.NewKVStoreKey("gov"),
keyFeeCollection: sdk.NewKVStoreKey("fee"),
keyParams: sdk.NewKVStoreKey("params"),
tkeyParams: sdk.NewTransientStoreKey("transient_params"),
}
// define the accountMapper
@ -85,10 +91,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
// add handlers
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes
app.Router().
@ -103,7 +110,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection)
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
app.MountStore(app.tkeyParams, sdk.StoreTypeTransient)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
@ -138,10 +146,10 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab
// application updates every end block
// nolint: unparam
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
tags, _ := gov.EndBlocker(ctx, app.govKeeper)
// Add these new validators to the addr -> pubkey map.
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
@ -168,15 +176,20 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
}
// load the initial stake information
err = stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
// load the address to pubkey map
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState())
return abci.ResponseInitChain{}
return abci.ResponseInitChain{
Validators: validators,
}
}
// export the state of gaia for a genesis file

View File

@ -1,9 +1,15 @@
package app
import (
"os"
"testing"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
abci "github.com/tendermint/tendermint/abci/types"
)
@ -31,3 +37,14 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
return nil
}
func TestGaiadExport(t *testing.T) {
db := db.NewMemDB()
gapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil)
setGenesis(gapp)
// Making a new app object with the db, so that initchain hasn't been called
newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil)
_, _, err := newGapp.ExportAppStateAndValidators()
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
}

View File

@ -3,19 +3,25 @@ package app
import (
"encoding/json"
"errors"
"fmt"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/config"
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/stake"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
)
// DefaultKeyPass contains the default key password for genesis transactions
const DefaultKeyPass = "12345678"
var (
// bonded tokens given to genesis validators/accounts
freeFermionVal = int64(100)
@ -81,30 +87,49 @@ type GaiaGenTx struct {
PubKey string `json:"pub_key"`
}
// Generate a gaia genesis transaction with flags
func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
// GaiaAppGenTx generates a Gaia genesis transaction.
func GaiaAppGenTx(
cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx,
) (appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
if genTxConfig.Name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
}
var addr sdk.AccAddress
var secret string
addr, secret, err = server.GenerateSaveCoinKey(genTxConfig.CliRoot, genTxConfig.Name, "1234567890", genTxConfig.Overwrite)
if err != nil {
return
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account '%s' (default %s):", genTxConfig.Name, DefaultKeyPass)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return appGenTx, cliPrint, validator, err
}
mm := map[string]string{"secret": secret}
var bz []byte
bz, err = cdc.MarshalJSON(mm)
if keyPass == "" {
keyPass = DefaultKeyPass
}
addr, secret, err := server.GenerateSaveCoinKey(
genTxConfig.CliRoot,
genTxConfig.Name,
keyPass,
genTxConfig.Overwrite,
)
if err != nil {
return
return appGenTx, cliPrint, validator, err
}
mm := map[string]string{"secret": secret}
bz, err := cdc.MarshalJSON(mm)
if err != nil {
return appGenTx, cliPrint, validator, err
}
cliPrint = json.RawMessage(bz)
appGenTx, _, validator, err = GaiaAppGenTxNF(cdc, pk, addr, genTxConfig.Name)
return
return appGenTx, cliPrint, validator, err
}
// Generate a gaia genesis transaction without flags

181
cmd/gaia/app/sim_test.go Normal file
View File

@ -0,0 +1,181 @@
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
numBlocks int
blockSize int
enabled bool
verbose bool
)
func init() {
flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed")
flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "Number of blocks")
flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block")
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
}
func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
var genesisAccounts []GenesisAccount
// Randomly generate some genesis accounts
for _, acc := range accs {
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}}
genesisAccounts = append(genesisAccounts, GenesisAccount{
Address: acc,
Coins: coins,
})
}
// Default genesis state
stakeGenesis := stake.DefaultGenesisState()
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.NewRat(100)
validator.DelegatorShares = sdk.NewRat(100)
delegation := stake.Delegation{accs[i], accs[i], sdk.NewRat(100), 0}
validators = append(validators, validator)
delegations = append(delegations, delegation)
}
stakeGenesis.Pool.LooseTokens = sdk.NewRat(int64(100*250) + (numInitiallyBonded * 100))
stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations
// No inflation, for now
stakeGenesis.Params.InflationMax = sdk.NewRat(0)
stakeGenesis.Params.InflationMin = sdk.NewRat(0)
genesis := GenesisState{
Accounts: genesisAccounts,
StakeData: stakeGenesis,
}
// Marshal genesis
appState, err := MakeCodec().MarshalJSON(genesis)
if err != nil {
panic(err)
}
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
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())
// Run randomized simulation
simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app),
[]simulation.RandSetup{},
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

@ -1,3 +1,5 @@
// +build cli_test
package clitest
import (
@ -23,7 +25,6 @@ import (
)
var (
pass = "1234567890"
gaiadHome = ""
gaiacliHome = ""
)
@ -33,11 +34,12 @@ func init() {
}
func TestGaiaCLISend(t *testing.T) {
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome))
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), pass)
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "")
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome))
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass)
// get a free port, also setup some common flags
servAddr, port, err := server.FreeTCPAddr()
@ -57,7 +59,7 @@ func TestGaiaCLISend(t *testing.T) {
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
@ -66,7 +68,7 @@ func TestGaiaCLISend(t *testing.T) {
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
// test autosequencing
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
@ -75,7 +77,7 @@ func TestGaiaCLISend(t *testing.T) {
require.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak").Int64())
// test memo
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), pass)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
@ -85,11 +87,11 @@ func TestGaiaCLISend(t *testing.T) {
}
func TestGaiaCLICreateValidator(t *testing.T) {
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome))
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), pass)
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "")
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome))
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass)
// get a free port, also setup some common flags
servAddr, port, err := server.FreeTCPAddr()
@ -107,7 +109,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
barAddr, barPubKey := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
barCeshPubKey := sdk.MustBech32ifyValPub(barPubKey)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
@ -118,12 +120,11 @@ func TestGaiaCLICreateValidator(t *testing.T) {
// create validator
cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags)
cvStr += fmt.Sprintf(" --from=%s", "bar")
cvStr += fmt.Sprintf(" --address-validator=%s", barAddr)
cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey)
cvStr += fmt.Sprintf(" --amount=%v", "2steak")
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
executeWrite(t, cvStr, pass)
executeWrite(t, cvStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
@ -137,10 +138,9 @@ func TestGaiaCLICreateValidator(t *testing.T) {
unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags)
unbondStr += fmt.Sprintf(" --from=%s", "bar")
unbondStr += fmt.Sprintf(" --address-validator=%s", barAddr)
unbondStr += fmt.Sprintf(" --address-delegator=%s", barAddr)
unbondStr += fmt.Sprintf(" --shares-amount=%v", "1")
success := executeWrite(t, unbondStr, pass)
success := executeWrite(t, unbondStr, app.DefaultKeyPass)
require.True(t, success)
tests.WaitForNextNBlocksTM(2, port)
@ -153,11 +153,11 @@ func TestGaiaCLICreateValidator(t *testing.T) {
}
func TestGaiaCLISubmitProposal(t *testing.T) {
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome))
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), pass)
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "")
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome))
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), pass)
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass)
// get a free port, also setup some common flags
servAddr, port, err := server.FreeTCPAddr()
@ -176,36 +176,80 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
executeWrite(t, fmt.Sprintf("gaiacli gov submit-proposal %v --proposer=%s --deposit=5steak --type=Text --title=Test --description=test --from=foo", flags, fooAddr), pass)
proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags), "")
require.Equal(t, "No matching proposals found", proposalsQuery)
// submit a test proposal
spStr := fmt.Sprintf("gaiacli gov submit-proposal %v", flags)
spStr += fmt.Sprintf(" --from=%s", "foo")
spStr += fmt.Sprintf(" --deposit=%s", "5steak")
spStr += fmt.Sprintf(" --type=%s", "Text")
spStr += fmt.Sprintf(" --title=%s", "Test")
spStr += fmt.Sprintf(" --description=%s", "test")
executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposalID=1 --output=json %v", flags))
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
executeWrite(t, fmt.Sprintf("gaiacli gov deposit %v --depositer=%s --deposit=10steak --proposalID=1 --from=foo", flags, fooAddr), pass)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags), "")
require.Equal(t, " 1 - Test", proposalsQuery)
depositStr := fmt.Sprintf("gaiacli gov deposit %v", flags)
depositStr += fmt.Sprintf(" --from=%s", "foo")
depositStr += fmt.Sprintf(" --deposit=%s", "10steak")
depositStr += fmt.Sprintf(" --proposal-id=%s", "1")
executeWrite(t, depositStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposalID=1 --output=json %v", flags))
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
executeWrite(t, fmt.Sprintf("gaiacli gov vote %v --proposalID=1 --voter=%s --option=Yes --from=foo", flags, fooAddr), pass)
voteStr := fmt.Sprintf("gaiacli gov vote %v", flags)
voteStr += fmt.Sprintf(" --from=%s", "foo")
voteStr += fmt.Sprintf(" --proposal-id=%s", "1")
voteStr += fmt.Sprintf(" --option=%s", "Yes")
executeWrite(t, voteStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
vote := executeGetVote(t, fmt.Sprintf("gaiacli gov query-vote --proposalID=1 --voter=%s --output=json %v", fooAddr, flags))
vote := executeGetVote(t, fmt.Sprintf("gaiacli gov query-vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
require.Equal(t, int64(1), vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option)
votes := executeGetVotes(t, fmt.Sprintf("gaiacli gov query-votes --proposalID=1 --output=json %v", flags))
votes := executeGetVotes(t, fmt.Sprintf("gaiacli gov query-votes --proposal-id=1 --output=json %v", flags))
require.Len(t, votes, 1)
require.Equal(t, int64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=DepositPeriod %v", flags), "")
require.Equal(t, "No matching proposals found", proposalsQuery)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=VotingPeriod %v", flags), "")
require.Equal(t, " 1 - Test", proposalsQuery)
// submit a second test proposal
spStr = fmt.Sprintf("gaiacli gov submit-proposal %v", flags)
spStr += fmt.Sprintf(" --from=%s", "foo")
spStr += fmt.Sprintf(" --deposit=%s", "5steak")
spStr += fmt.Sprintf(" --type=%s", "Text")
spStr += fmt.Sprintf(" --title=%s", "Apples")
spStr += fmt.Sprintf(" --description=%s", "test")
executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --latest=1 %v", flags), "")
require.Equal(t, " 2 - Apples", proposalsQuery)
}
//___________________________________________________________________________________
@ -247,7 +291,7 @@ func executeWrite(t *testing.T, cmdStr string, writes ...string) bool {
}
func executeInit(t *testing.T, cmdStr string) (chainID string) {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, app.DefaultKeyPass)
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
@ -260,7 +304,7 @@ func executeInit(t *testing.T, cmdStr string) (chainID string) {
}
func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKey) {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var ko keys.KeyOutput
keys.UnmarshalJSON([]byte(out), &ko)
@ -271,7 +315,7 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKe
}
func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
require.NoError(t, err, "out %v, err %v", out, err)
@ -285,7 +329,7 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
}
func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var validator stake.Validator
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &validator)
@ -294,7 +338,7 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
}
func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var proposal gov.Proposal
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &proposal)
@ -303,7 +347,7 @@ func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
}
func executeGetVote(t *testing.T, cmdStr string) gov.Vote {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var vote gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &vote)
@ -312,7 +356,7 @@ func executeGetVote(t *testing.T, cmdStr string) gov.Vote {
}
func executeGetVotes(t *testing.T, cmdStr string) []gov.Vote {
out := tests.ExecuteT(t, cmdStr)
out := tests.ExecuteT(t, cmdStr, "")
var votes []gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &votes)

3
cmd/gaia/cli_test/doc.go Normal file
View File

@ -0,0 +1,3 @@
package clitest
// package clitest runs integration tests which make use of CLI commands.

View File

@ -88,6 +88,10 @@ func main() {
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc),
stakecmd.GetCmdQueryRedelegation("stake", cdc),
stakecmd.GetCmdQueryRedelegations("stake", cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
)...)
stakeCmd.AddCommand(
@ -113,6 +117,7 @@ func main() {
govcmd.GetCmdQueryProposal("gov", cdc),
govcmd.GetCmdQueryVote("gov", cdc),
govcmd.GetCmdQueryVotes("gov", cdc),
govcmd.GetCmdQueryProposals("gov", cdc),
)...)
govCmd.AddCommand(
client.PostCommands(

View File

@ -25,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -134,6 +135,7 @@ type GaiaApp struct {
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper auth.AccountMapper
@ -142,12 +144,13 @@ type GaiaApp struct {
ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
paramsKeeper params.Keeper
}
func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
cdc := MakeCodec()
bApp := bam.NewBaseApp(appName, cdc, logger, db, baseAppOptions...)
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(os.Stdout)
// create your application object
@ -159,6 +162,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
keyParams: sdk.NewKVStoreKey("params"),
}
// define the accountMapper
@ -171,8 +175,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
// add handlers
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes
app.Router().
@ -191,6 +196,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
cmn.Exit(err.Error())
}
app.Seal()
return app
}
@ -245,10 +252,12 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
}
// load the initial stake information
err = stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "")
}
return abci.ResponseInitChain{}
return abci.ResponseInitChain{
Validators: validators,
}
}

View File

@ -5,5 +5,3 @@ The content of this file was moved to the `/docs` folder and is hosted on the
The rest of this folder was moved to the [testnets
repo](https://github.com/cosmos/testnets).

View File

@ -2,6 +2,11 @@
See [testnets repo](https://github.com/cosmos/testnets).
## *July 22, 2018, 5:30 EST* - Gaia-7001 Consensus Failure
- [Consensus Failure at Block 24570](https://github.com/cosmos/cosmos-sdk/issues/1787)
## *July 17, 2018, 4:00 EST* - New Testnet Gaia-7001
- New testnet with fixes for the genesis file

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

@ -15,12 +15,14 @@ type byter interface {
Bytes() []byte
}
func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) {
func checkAminoBinary(t *testing.T, src, dst interface{}, size int) {
// Marshal to binary bytes.
bz, err := cdc.MarshalBinaryBare(src)
require.Nil(t, err, "%+v", err)
// Make sure this is compatible with current (Bytes()) encoding.
require.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch")
if byterSrc, ok := src.(byter); ok {
// Make sure this is compatible with current (Bytes()) encoding.
require.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch")
}
// Make sure we have the expected length.
if size != -1 {
require.Equal(t, size, len(bz), "Amino binary size mismatch")
@ -55,8 +57,6 @@ func ExamplePrintRegisteredTypes() {
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | |
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | |
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | |
//| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | |
//| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | |
}
func TestKeyEncodings(t *testing.T) {
@ -86,13 +86,11 @@ func TestKeyEncodings(t *testing.T) {
require.EqualValues(t, tc.privKey, priv3)
// Check (de/en)codings of Signatures.
var sig1, sig2, sig3 tcrypto.Signature
var sig1, sig2 []byte
sig1, err := tc.privKey.Sign([]byte("something"))
require.NoError(t, err)
checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways.
require.EqualValues(t, sig1, sig2)
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
require.EqualValues(t, sig1, sig3)
// Check (de/en)codings of PubKeys.
pubKey := tc.privKey.PubKey()
@ -107,7 +105,7 @@ func TestKeyEncodings(t *testing.T) {
func TestNilEncodings(t *testing.T) {
// Check nil Signature.
var a, b tcrypto.Signature
var a, b []byte
checkAminoJSON(t, &a, &b, true)
require.EqualValues(t, a, b)

View File

@ -199,7 +199,7 @@ func (kb dbKeybase) Get(name string) (Info, error) {
// Sign signs the msg with the named key.
// It returns an error if the key doesn't exist or the decryption fails.
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig tmcrypto.Signature, pub tmcrypto.PubKey, err error) {
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
info, err := kb.Get(name)
if err != nil {
return

View File

@ -13,6 +13,10 @@ import (
dbm "github.com/tendermint/tendermint/libs/db"
)
func init() {
BcryptSecurityParameter = 1
}
// TestKeyManagement makes sure we can manipulate these keys well
func TestKeyManagement(t *testing.T) {
// make the storage with reasonable defaults
@ -142,7 +146,7 @@ func TestSignVerify(t *testing.T) {
cases := []struct {
key crypto.PubKey
data []byte
sig crypto.Signature
sig []byte
valid bool
}{
// proper matches

View File

@ -16,7 +16,7 @@ type Keybase interface {
Delete(name, passphrase string) error
// Sign some bytes, looking up the private key to use
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)
// CreateMnemonic creates a new mnemonic, and derives a hierarchical deterministic
// key from that.
@ -44,10 +44,31 @@ type Keybase interface {
ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error)
}
// KeyType reflects a human-readable type for key listing.
type KeyType uint
// Info KeyTypes
const (
TypeLocal KeyType = 0
TypeLedger KeyType = 1
TypeOffline KeyType = 2
)
var keyTypes = map[KeyType]string{
TypeLocal: "local",
TypeLedger: "ledger",
TypeOffline: "offline",
}
// String implements the stringer interface for KeyType.
func (kt KeyType) String() string {
return keyTypes[kt]
}
// Info is the publicly exposed information about a keypair
type Info interface {
// Human-readable type for key listing
GetType() string
GetType() KeyType
// Name of the key
GetName() string
// Public key
@ -73,8 +94,8 @@ func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
}
}
func (i localInfo) GetType() string {
return "local"
func (i localInfo) GetType() KeyType {
return TypeLocal
}
func (i localInfo) GetName() string {
@ -100,8 +121,8 @@ func newLedgerInfo(name string, pub crypto.PubKey, path ccrypto.DerivationPath)
}
}
func (i ledgerInfo) GetType() string {
return "ledger"
func (i ledgerInfo) GetType() KeyType {
return TypeLedger
}
func (i ledgerInfo) GetName() string {
@ -125,8 +146,8 @@ func newOfflineInfo(name string, pub crypto.PubKey) Info {
}
}
func (i offlineInfo) GetType() string {
return "offline"
func (i offlineInfo) GetType() KeyType {
return TypeOffline
}
func (i offlineInfo) GetName() string {

View File

@ -114,7 +114,7 @@ func (pkl PrivKeyLedgerSecp256k1) Equals(other tmcrypto.PrivKey) bool {
// Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning
// an error, so this should only trigger if the private key is held in memory
// for a while before use.
func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) (tmcrypto.Signature, error) {
func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) ([]byte, error) {
sig, err := pkl.signLedgerSecp256k1(msg)
if err != nil {
return nil, err
@ -135,13 +135,8 @@ func (pkl PrivKeyLedgerSecp256k1) getPubKey() (key tmcrypto.PubKey, err error) {
return key, err
}
func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) (tmcrypto.Signature, error) {
sigBytes, err := pkl.ledger.SignSECP256K1(pkl.Path, msg)
if err != nil {
return nil, err
}
return tmsecp256k1.SignatureSecp256k1FromBytes(sigBytes), nil
func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) ([]byte, error) {
return pkl.ledger.SignSECP256K1(pkl.Path, msg)
}
func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey, err error) {
@ -154,6 +149,9 @@ func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey,
// re-serialize in the 33-byte compressed format
cmp, err := secp256k1.ParsePubKey(key[:], secp256k1.S256())
if err != nil {
return nil, fmt.Errorf("error parsing public key: %v", err)
}
copy(pk[:], cmp.SerializeCompressed())
return pk, nil

17
docs/DOCS_README.md Normal file
View File

@ -0,0 +1,17 @@
# Documentation Maintenance Overview
The documentation found in this directory is hosted at:
- https://cosmos.network/docs/
and built using [VuePress](https://vuepress.vuejs.org/) from the Cosmos website repo:
- https://github.com/cosmos/cosmos.network
which has a [configuration file](https://github.com/cosmos/cosmos.network/blob/develop/docs/.vuepress/config.js) for displaying
the Table of Contents that lists all the documentation.
Under the hood, Jenkins listens for changes in ./docs then pushes a `docs-staging` branch to the cosmos.network repo with the latest documentation. That branch must be manually PR'd to `develop` then `master` for staging then production. This process should happen in synchrony with a release.
The `README.md` in this directory is the landing page for
website documentation.

78
docs/PRIORITIES.md Normal file
View File

@ -0,0 +1,78 @@
## Fees
- Collection
- Gas price based on parameter
- (which gets changed automatically)
- https://github.com/cosmos/cosmos-sdk/issues/1921
- Per block gas usage as %
- Windowing function
- Block N,
- For Block N-x ~ N, get average of %
- Should take into account time.
- Standard for querying this price // needs to be used by UX.
- Distribution
- MVP: 1 week, 1 week for testing.
## Governance v2
- V1 is just text proposals
- Software upgrade stuff
- https://github.com/cosmos/cosmos-sdk/issues/1734#issuecomment-407254938
- https://github.com/cosmos/cosmos-sdk/issues/1079
- We need to test auto-flipping w/ threshold voting power.
- Super simple:
- Only use text proposals
- On-chain mechanism for agreeing on when to "flip" to new functionality
## Staking/Slashing/Stability
- Unbonding state for validators https://github.com/cosmos/cosmos-sdk/issues/1676
- current: downtime, double signing during unbonding
- who gets slashed when -- needs review about edge cases
- need to communicate to everyone that lite has this edge case
- Issues:
- https://github.com/cosmos/cosmos-sdk/issues/1378
- https://github.com/cosmos/cosmos-sdk/issues/1348
- https://github.com/cosmos/cosmos-sdk/issues/1440
* Est Difficulty: Hard
* _*Note:*_ This feature needs to be fully fleshed out. Will require a meeting between @jaekwon, @cwgoes, @rigel, @zaki, @bucky to discuss the issues. @jackzampolin to facilitate.
## Vesting
- 24 accounts with NLocktime
- “No funds can be transferred before timelock”
- New atoms and such can be withdrawn right way
- Requires being able to send fees and inflation to new account
## Multisig
- Make it work with Cli
- ADR
## Reserve Pool
- No withdrawing from it at launch
## Other:
- Need to update for NextValidatorSet - need to upgrade SDK for it
- Need to update for new ABCI changes - error string, tags are list of lists, proposer in header
- Inflation ?
## Gas
- Calculate gas
## Reward proposer
- Requires tendermint changes
# Lower priority
## Circuit Breaker
- Kinda needed for enabling txs.
## Governance proposal changes
- V2 is parameter changes
## Slashing/Stability
- tendermint evidence: we dont yet slash byzantine signatures (signing at all) when not bonded.
# Other priority
## gaiad // gaiacli
- Documentation // language
## gaialite
- Documentation // language

11
docs/RELEASE_PROCESS.md Normal file
View File

@ -0,0 +1,11 @@
### `cosmos/cosmos-sdk` Release Process
- [ ] 1. Decide on release designation (are we doing a patch, or minor version bump) and start a P.R. for the release
- [ ] 2. Add commits/PRs that are desired for this release **that havent already been added to develop**
- [ ] 3. Merge items in `PENDING.md` into the `CHANGELOG.md`. While doing this make sure that each entry contains links to issues/PRs for each item
- [ ] 4. Summarize breaking API changes section under “Breaking Changes” section to the `CHANGELOG.md` to bring attention to any breaking API changes that affect RPC consumers.
- [ ] 5. Tag the commit `{{ .Release.Name }}-rcN`
- [ ] 6. Kick off 1 day of automated fuzz testing
- [ ] 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

@ -1,8 +1,26 @@
swagger: '2.0'
info:
version: '1.1.0'
title: Light client daemon to interface with Cosmos baseserver via REST
description: Specification for the LCD provided by `gaiacli advanced rest-server`
title: Gaia-Lite (former LCD) to interface with Cosmos BaseServer via REST
description: Specification for Gaia-lite provided by `gaiacli advanced rest-server`
tags:
- name: keys
description: Key management to add or view local private keys
- name: send
description: Create and sign a send tx
- name: stake
description: Stake module API for staking and validation
- name: account
description: Query account balance
- name: query
description: Information about blocks and txs
- name: validator set
description: Check the state of the validator set
- name: node
description: Information of the connected node
- name: version
description: Information about the app version
securityDefinitions:
@ -12,22 +30,28 @@ securityDefinitions:
paths:
/version:
get:
summary: Version of the light client daemon
description: Get the version of the LCD running locally to compare against expected
summary: Version of Gaia-lite
tags:
- version
description: Get the version of gaia-lite running locally to compare against expected
responses:
200:
description: Plaintext version i.e. "v0.5.0"
/node_version:
get:
summary: Version of the connected node
tags:
- node
description: Get the version of the SDK running on the connected node to compare against expected
responses:
200:
description: Plaintext version i.e. "v0.5.0"
/node_info:
get:
description: Only the node info. Block information can be queried via /block/latest
summary: The propertied of the connected node
description: Information about the connected node
summary: The properties of the connected node
tags:
- node
produces:
- application/json
responses:
@ -61,6 +85,8 @@ paths:
/syncing:
get:
summary: Syncing state of node
tags:
- node
description: Get if the node is currently syning with other nodes
responses:
200:
@ -69,6 +95,8 @@ paths:
/keys:
get:
summary: List of accounts stored locally
tags:
- keys
produces:
- application/json
responses:
@ -80,6 +108,8 @@ paths:
$ref: '#/definitions/Account'
post:
summary: Create a new account locally
tags:
- keys
consumes:
- application/json
parameters:
@ -105,6 +135,8 @@ paths:
/keys/seed:
get:
summary: Create a new seed to create a new account with
tags:
- keys
produces:
- application/json
responses:
@ -121,6 +153,8 @@ paths:
type: string
get:
summary: Get a certain locally stored account
tags:
- keys
produces:
- application/json
responses:
@ -132,6 +166,8 @@ paths:
description: Account is not available
put:
summary: Update the password for this account in the KMS
tags:
- keys
consumes:
- application/json
parameters:
@ -157,6 +193,8 @@ paths:
description: Account is not available
delete:
summary: Remove an account
tags:
- keys
consumes:
- application/json
parameters:
@ -216,6 +254,8 @@ paths:
type: string
get:
summary: Get the account balances
tags:
- account
produces:
- application/json
responses:
@ -234,6 +274,8 @@ paths:
type: string
post:
summary: Send coins (build -> sign -> send)
tags:
- send
security:
- kms: []
consumes:
@ -265,6 +307,8 @@ paths:
/blocks/latest:
get:
summary: Get the latest block
tags:
- query
produces:
- application/json
responses:
@ -281,6 +325,8 @@ paths:
type: number
get:
summary: Get a block at a certain height
tags:
- query
produces:
- application/json
responses:
@ -293,6 +339,8 @@ paths:
/validatorsets/latest:
get:
summary: Get the latest validator set
tags:
- validator set
produces:
- application/json
responses:
@ -316,6 +364,8 @@ paths:
type: number
get:
summary: Get a validator set a certain height
tags:
- validator set
produces:
- application/json
responses:
@ -407,6 +457,8 @@ paths:
type: string
get:
summary: Get a Tx by hash
tags:
- query
produces:
- application/json
responses:
@ -416,141 +468,201 @@ paths:
$ref: "#/definitions/Tx"
404:
description: Tx not available for provided hash
# /delegates:
# parameters:
# - in: query
# name: delegator
# description: Query for all delegates a delegator has stake with
# schema:
# $ref: "#/definitions/Address"
# get:
# summary: Get a list of canidates/delegates/validators (optionally filtered by delegator)
# responses:
# 200:
# description: List of delegates, filtered by provided delegator address
# content:
# application/json:
# schema:
# type: array
# items:
# $ref: "#/definitions/Delegate"
# /delegates/bond:
# post:
# summary: Bond atoms (build -> sign -> send)
# security:
# - sign: []
# requestBody:
# content:
# application/json:
# schema:
# type: array
# items:
# type: object
# properties:
# amount:
# $ref: "#/definitions/Coins"
# pub_key:
# $ref: "#/definitions/PubKey"
# responses:
# 202:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
# /delegates/unbond:
# post:
# summary: Unbond atoms (build -> sign -> send)
# security:
# - sign: []
# requestBody:
# content:
# application/json:
# schema:
# type: array
# items:
# type: object
# properties:
# amount:
# $ref: "#/definitions/Coins"
# pub_key:
# $ref: "#/definitions/PubKey"
# responses:
# 202:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
# /delegates/{pubkey}:
# parameters:
# - in: path
# name: pubkey
# description: Pubkey of a delegate
# required: true
# schema:
# type: string
# example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
# get:
# summary: Get a certain canidate/delegate/validator
# responses:
# 200:
# description: Delegate for specified pub_key
# content:
# application/json:
# schema:
# $ref: "#/definitions/Delegate"
# 404:
# description: No delegate found for provided pub_key
# /delegates/{pubkey}/bond:
# parameters:
# - in: path
# name: pubkey
# description: Pubkey of a delegate
# required: true
# schema:
# type: string
# example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
# post:
# summary: Bond atoms (build -> sign -> send)
# security:
# - sign: []
# requestBody:
# content:
# application/json:
# schema:
# type: object
# properties:
# amount:
# $ref: "#/definitions/Coins"
# responses:
# 202:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
# /delegates/{pubkey}/unbond:
# parameters:
# - in: path
# name: pubkey
# description: Pubkey of a delegate
# required: true
# schema:
# type: string
# example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
# post:
# summary: Unbond atoms (build -> sign -> send)
# security:
# - sign: []
# requestBody:
# content:
# application/json:
# schema:
# type: object
# properties:
# amount:
# $ref: "#/definitions/Coins"
# responses:
# 202:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
# ================== Staking Module # ==================
# TODO create D
/stake/delegators/{delegatorAddr}:
parameters:
- in: path
name: delegatorAddr
description: AccAddress of Delegator
required: true
type: string
get:
summary: Get all delegations (delegation, undelegation) from a delegator
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
404:
description: Not Found
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/txs:
parameters:
- in: path
name: delegatorAddr
description: AccAddress of Delegator
required: true
type: string
get:
summary: Get all staking txs (i.e msgs) from a delegator
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/Tx"
404:
description: Not Found
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/delegations:
parameters:
- in: path
name: delegatorAddr
description: Bech32 AccAddress of Delegator
required: true
type: string
post:
summary: Submit delegation
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
schema:
type: object
properties:
name:
type: string
password:
type: string
account_number:
type: number
delegations:
type: array
items:
type: string
begin_unbondings:
type: array
items:
type: string
complete_unbondings:
type: array
items:
type: string
begin_redelegates:
type: array
items:
type: string
complete_redelegates:
type: array
items:
type: string
chain_id:
type: string
gas:
type: number
sequence:
type: number
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/Tx"
404:
description: Not Found
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}:
parameters:
- in: path
name: delegatorAddr
description: Bech32 AccAddress of Delegator
required: true
type: string
- in: path
name: validatorAddr
description: Bech32 ValAddress of Delegator
required: true
type: string
get:
summary: Query the current delegation status between a delegator and a validator
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
404:
description: Not Found
/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}:
parameters:
- in: path
name: delegatorAddr
description: Bech32 AccAddress of Delegator
required: true
type: string
- in: path
name: validatorAddr
description: Bech32 ValAddress of Delegator
required: true
type: string
get:
summary: Query all unbonding delegations between a delegator and a validator
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
404:
description: Not Found
500:
description: Internal Server Error
/stake/validators:
get:
summary: Get all validator candidates
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
500:
description: Internal Server Error
/stake/validators/{validatorAddr}:
parameters:
- in: path
name: validatorAddr
description: Bech32 ValAddress of Delegator
required: true
type: string
get:
summary: Query the information from a single validator
tags:
- stake
produces:
- application/json
responses:
200:
description: OK
404:
description: Not Found
500:
description: Internal Server Error
# TODO Add staking definitions
definitions:
Address:
type: string

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,27 @@
## Tendermint and Cosmos
Blockchains can be divided into three conceptual layers:
- **Networking:** Responsible for propagating transactions.
- **Consensus:** Enables validator nodes to agree on the next set of transactions to process (i.e. add blocks of transactions to the blockchain).
- **Application:** Responsible for updating the state given a set of transactions, i.e. processing transactions.
The *networking* layer makes sure that each node receives transactions. The *consensus* layer makes sure that each node agrees on the same transactions to modify their local state. As for the *application* layer, it processes transactions. Given a transaction and a state, the application will return a new state. In Bitcoin for example, the application state is a ledger or list of balances for each account (in reality, it's a list of UTXO, short for Unspent Transaction Output, but let's call them balances for the sake of simplicity), and the transactions modify the application's state by changing these list of balances. In the case of Ethereum, the application is a virtual machine. Each transaction goes through this virtual machine and modifies the application state according to the the smart contract that is called within it.
Before Tendermint, building a blockchain required building all three layers from the ground up. It was such a tedious task that most developers preferred to fork or replicate the Bitcoin codebase, but were constrainted by the limitations of Bitcoin's protocol. The Ethereum Virtual Machine (EVM) was designed to solve this problem and simplify decentralized application development by allowing customizable logic to be executed through smart contracts. But it did not resolve the limitations (interoperability, scalability and customization) of blockchains themselves. Go-Ethereum remains a very monolithic tech stack that is difficult to hard-fork much like Bitcoin's codebase.
Tendermint was designed to address these issues and provide developers with an laternative. The goal of Tendermint is to provide the *networking* and *consensus* layers of a blockchain as a generic engine to power any application developers want to build. With Tendermint, developers only have to focus on the *application* layer, thereby saving hundreds of hours of work and costly development set-ups. For reference, Tendermint also designates the name of the byzantine fault tolerant consensus algorithm used within the Tendermint Core engine.
Tendermint connects the blockchain engine, Tendermint Core (*networking* and *consensus* layers), to the *application* layer via a socket protocol called the [ABCI](https://github.com/tendermint/abci), short for Application-BlockChain Interface. Developers only have to implement a few messages to build an ABCI-enabled application that runs on top of the Tendermint Core engine. ABCI is language agnostic, meaning that developers can build the *application* part of their blockchain in any programming language. Building on top of the Tendermint Core engine also provides the following benefits:
- **Public or private blockchain capable.** Developers can deploy any blockchain application, permissioned (private) and permissionless (public), on top of Tendermint Core.
- **Performance.** Tendermint Core is a state-of-the-art blockchain consensus engine able to handle large number of transactions in short timespan. A block time on Tendermint Core can be as low as one second and can process thousands of transactions in that time period.
- **Instant finality.** A property of the Tendermint consensus algorithm is instant finality, meaning that forks are never created, as long as less than a third of the validators are malicious (byzantine). Users can be sure their transactions are finalized as soon as a block is created.
- **Security.** Tendermint Core's consensus is not only fault tolerant, its optimally Byzantine fault-tolerant (BFT), with accountability. If the blockchain forks, there is a way to determine liability.
- **Light-client support**. Tendermint provides built-in light-clients.
But most importantly, Tendermint is natively compatible with the [Inter-Blockchain Communication Protocol](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/ibc) (IBC). This means that any Tendermint-based blockchain, whether public or private, can be natively connected to the Cosmos ecosystem and securely exchange tokens with other blockchains in the ecosystem. Note that benefiting from interoperability via IBC and Cosmos preserves the sovereignty of your Tendermint chain. Non-Tendermint chains can also be connected to Cosmos via IBC adapters or Peg-Zones, but this is out of scope for this document.
For a more detailed overview of the Cosmos ecosystem, you can read [this article](https://blog.cosmos.network/understanding-the-value-proposition-of-cosmos-ecaef63350d).
For more on Tendermint, go [here](tendermint.md)

View File

@ -5,9 +5,43 @@ Tendermint is software for securely and consistently replicating an application
Tendermint is designed to be easy-to-use, simple-to-understand, highly performant, and useful for a wide variety of distributed applications.
## Byzantine Fault Tolerance
The ability to tolerate machines failing in arbitrary ways, including becoming malicious, is known as Byzantine fault tolerance (BFT). The theory of BFT is decades old, but software implementations have only became popular recently, due largely to the success of “blockchain technology” like Bitcoin and Ethereum. Blockchain technology is just a re-formalization of BFT in a more modern setting, with emphasis on peer-to-peer networking and cryptographic authentication. The name derives from the way transactions are batched in blocks, where each block contains a cryptographic hash of the previous one, forming a chain. In practice, the blockchain data structure actually optimizes BFT design.
## Application Blockchain Interface
Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html#abci-overview) for a deep dive into the ABCI.
The [Cosmos SDK](/sdk/overview.md) is an ABCI framework written in Go. [Lotion JS](/lotion/overview.md) is an ABCI framework written in JavaScript.
## Understanding the roles of the different layers
It is important to have a good understanding of the respective responsibilities of both the *Application* and the *Consensus Engine*.
Responsibilities of the *Consensus Engine*:
- Propagate transactions
- Agree on the order of valid transactions
Reponsibilities of the *Application*:
- Generate Transactions
- Check if transactions are valid
- Process Transactions (includes state changes)
It is worth underlining that the *Consensus Engine* has knowledge of a given validator set for each block, but that it is the responsiblity of the *Application* to trigger validator set changes. This is the reason why it is possible to build both **public and private chains** with the Cosmos-SDK and Tendermint. A chain will be public or private depending on the rules, defined at application level, that governs a validator's set changes.
The ABCI establishes the connection between the *Consensus Engine* and the *Application*. Essentially, it boils down to two messages:
- `CheckTx`: Ask the application if the transaction is valid. When a validator's node receives a transaction, it will run `CheckTx` on it. If the transaction is valid, it is added to the mempool.
- `DeliverTx`: Ask the application to process the transaction and update the state.
Let us give a high-level overview of how the *Consensus Engine* and the *Application* interract with each other.
- At all times, when the consensus engine (Tendermint Core) of a validator node receives a transaction, it passes it to the application via `CheckTx` to check its validity. If it is valid, the transaction is added to the mempool.
- Let us say we are at block N. There is a validator set V. A proposer of the next block is selected from V by the *Consensus Engine*. The proposer gathers valid transaction from its mempool to form a new block. Then, the block is gossiped to other validators to be signed/commited. The block becomes block N+1 once 2/3+ of V have signed a *precommit* on it (For a more detailed explanation of the consensus algorithm, click [here](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm)).
- When block N+1 is signed by 2/3+ of V, it is gossipped to full-nodes. When full-nodes receive the block, they confirm its validity. A block is valid if it it holds valid signatures from more than 2/3 of V and if all the transactions in the block are valid. To check the validity of transactions, the *Consensus Engine* transfers them to the application via `DeliverTx`. After each transaction, `DeliverTx` returns a new state if the transaction was valid. At the end of the block, a final state is committed. Of course, this means that the order of transaction within a block matters.
## Application frameworks
Even if Tendermint makes it easy for developers to build their own blockchain by enabling them to focus on the *Application* layer of their blockchain, building an *Application* can be a challenging task in itself. This is why *Application Frameworks* exist. They provide developers with a secure and features-heavy environment to develop Tendermint-based applications. Here are some examples of *Application Frameworks* :
- The [Cosmos SDK](/sdk/overview.md) is an ABCI framework written in Go.
- [Lotion JS](/lotion/overview.md) is an ABCI framework written in JavaScript.

View File

@ -4,4 +4,4 @@ Cosmos is a decentralized network of independent parallel blockchains, each powe
The first blockchain in the Cosmos Network is the [Cosmos Hub](), whose native token is the Atom. Cosmos is a permission-less network, meaning that anybody can build a blockchain on it.
Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.
Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.

660
docs/light/api.md Normal file
View File

@ -0,0 +1,660 @@
# Cosmos Hub (Gaia) LCD API
This document describes the API that is exposed by the specific LCD implementation of the Cosmos
Hub (Gaia). Those APIs are exposed by a REST server and can easily be accessed over HTTP/WS(websocket)
connections.
The complete API is comprised of the sub-APIs of different modules. The modules in the Cosmos Hub
(Gaia) API are:
* ICS0 (TendermintAPI)
* ICS1 (KeyAPI)
* ICS20 (TokenAPI)
* ICS21 (StakingAPI) - not yet implemented
* ICS22 (GovernanceAPI) - not yet implemented
Error messages my change and should be only used for display purposes. Error messages should not be
used for determining the error type.
## ICS0 - TendermintAPI - not yet implemented
Exposes the same functionality as the Tendermint RPC from a full node. It aims to have a very
similar API.
### /broadcast_tx_sync - POST
url: /broadcast_tx_sync
Functionality: Submit a signed transaction synchronously. This returns a response from CheckTx.
Parameters:
| Parameter | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | --------------- |
| transaction | string | null | true | signed tx bytes |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"code":0,
"hash":"0D33F2F03A5234F38706E43004489E061AC40A2E",
"data":"",
"log":""
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not submit the transaction synchronously.",
"result":{}
}
```
### /broadcast_tx_async - POST
url: /broadcast_tx_async
Functionality: Submit a signed transaction asynchronously. This does not return a response from CheckTx.
Parameters:
| Parameter | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | --------------- |
| transaction | string | null | true | signed tx bytes |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result": {
"code":0,
"hash":"E39AAB7A537ABAA237831742DCE1117F187C3C52",
"data":"",
"log":""
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not submit the transaction asynchronously.",
"result":{}
}
```
### /broadcast_tx_commit - POST
url: /broadcast_tx_commit
Functionality: Submit a signed transaction and waits for it to be committed in a block.
Parameters:
| Parameter | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | --------------- |
| transaction | string | null | true | signed tx bytes |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"height":26682,
"hash":"75CA0F856A4DA078FC4911580360E70CEFB2EBEE",
"deliver_tx":{
"log":"",
"data":"",
"code":0
},
"check_tx":{
"log":"",
"data":"",
"code":0
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not commit the transaction.",
"result":{}
}
```
## ICS1 - KeyAPI
This API exposes all functionality needed for key creation, signing and management.
### /keys - GET
url: /keys
Functionality: Gets a list of all the keys.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"keys":[
{
"name":"monkey",
"address":"cosmosaccaddr1fedh326uxqlxs8ph9ej7cf854gz7fd5zlym5pd",
"pub_key":"cosmosaccpub1zcjduc3q8s8ha96ry4xc5xvjp9tr9w9p0e5lk5y0rpjs5epsfxs4wmf72x3shvus0t"
},
{
"name":"test",
"address":"cosmosaccaddr1thlqhjqw78zvcy0ua4ldj9gnazqzavyw4eske2",
"pub_key":"cosmosaccpub1zcjduc3qyx6hlf825jcnj39adpkaxjer95q7yvy25yhfj3dmqy2ctev0rxmse9cuak"
}
],
"block_height":5241
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not retrieve the keys.",
"result":{}
}
```
### /keys/recover - POST
url: /keys/recover
Functionality: Recover your key from seed and persist it encrypted with the password.
Parameter:
| Parameter | Type | Default | Required | Description |
| --------- | ------ | ------- | -------- | ---------------- |
| name | string | null | true | name of key |
| password | string | null | true | password of key |
| seed | string | null | true | seed of key |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"address":"BD607C37147656A507A5A521AA9446EB72B2C907"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not recover the key.",
"result":{}
}
```
### /keys/create - POST
url: /keys/create
Functionality: Create a new key.
Parameter:
| Parameter | Type | Default | Required | Description |
| --------- | ------ | ------- | -------- | ---------------- |
| name | string | null | true | name of key |
| password | string | null | true | password of key |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"seed":"crime carpet recycle erase simple prepare moral dentist fee cause pitch trigger when velvet animal abandon"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create new key.",
"result":{}
}
```
### /keys/{name} - GET
url: /keys/{name}
Functionality: Get the information for the specified key.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"name":"test",
"address":"cosmosaccaddr1thlqhjqw78zvcy0ua4ldj9gnazqzavyw4eske2",
"pub_key":"cosmosaccpub1zcjduc3qyx6hlf825jcnj39adpkaxjer95q7yvy25yhfj3dmqy2ctev0rxmse9cuak"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not find information on the specified key.",
"result":{}
}
```
### /keys/{name} - PUT
url: /keys/{name}
Functionality: Change the encryption password for the specified key.
Parameters:
| Parameter | Type | Default | Required | Description |
| --------------- | ------ | ------- | -------- | --------------- |
| old_password | string | null | true | old password |
| new_password | string | null | true | new password |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not update the specified key.",
"result":{}
}
```
### /keys/{name} - DELETE
url: /keys/{name}
Functionality: Delete the specified key.
Parameters:
| Parameter | Type | Default | Required | Description |
| --------- | ------ | ------- | -------- | ---------------- |
| password | string | null | true | password of key |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not delete the specified key.",
"result":{}
}
```
## ICS20 - TokenAPI
The TokenAPI exposes all functionality needed to query account balances and send transactions.
### /bank/balance/{account} - GET
url: /bank/balance/{account}
Functionality: Query the specified account.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result": {
"atom":1000,
"photon":500,
"ether":20
}
}
```
Returns on error:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not find any balance for the specified account.",
"result":{}
}
```
### /bank/create_transfer - POST
url: /bank/create_transfer
Functionality: Create a transfer in the bank module.
Parameters:
| Parameter | Type | Default | Required | Description |
| ------------ | ------ | ------- | -------- | ------------------------- |
| sender | string | null | true | Address of sender |
| receiver | string | null | true | address of receiver |
| chain_id | string | null | true | chain id |
| amount | int | null | true | amount of the token |
| denomonation | string | null | true | denomonation of the token |
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO:<JSON sign bytes for the transaction>"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
## ICS21 - StakingAPI
The StakingAPI exposes all functionality needed for validation and delegation in Proof-of-Stake.
### /stake/delegators/{delegatorAddr} - GET
url: /stake/delegators/{delegatorAddr}
Functionality: Get all delegations (delegation, undelegation) from a delegator.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result": {
"atom":1000,
"photon":500,
"ether":20
}
}
```
Returns on error:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not find any balance for the specified account.",
"result":{}
}
```
### /stake/delegators/{delegatorAddr}/txs - GET
url: /stake/delegators/{delegatorAddr}/txs
Functionality: Get all staking txs (i.e msgs) from a delegator.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
### /stake/delegators/{delegatorAddr}/delegations - POST
url: /stake/delegators/{delegatorAddr}/delegations
Functionality: Submit a delegation.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
### /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} - GET
url: /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}
Functionality: Query the current delegation status between a delegator and a validator.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
### /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} - GET
url: /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}
Functionality: Query all unbonding delegations between a delegator and a validator.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
### /stake/validators - GET
url: /stake/validators
Functionality: Get all validator candidates.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```
### /stake/validators/{validatorAddr} - GET
url: /stake/validators/{validatorAddr}
Functionality: Query the information from a single validator.
Returns on success:
```json
{
"rest api":"2.0",
"code":200,
"error":"",
"result":{
"transaction":"TODO"
}
}
```
Returns on failure:
```json
{
"rest api":"2.0",
"code":500,
"error":"Could not create the transaction.",
"result":{}
}
```

View File

@ -0,0 +1,40 @@
# Getting Started
To start a rest server, we need to specify the following parameters:
| Parameter | Type | Default | Required | Description |
| ----------- | --------- | ----------------------- | -------- | ---------------------------------------------------- |
| chain-id | string | null | true | chain id of the full node to connect |
| node | URL | "tcp://localhost:46657" | true | address of the full node to connect |
| laddr | URL | "tcp://localhost:1317" | true | address to run the rest server on |
| trust-node | bool | "false" | true | Whether this LCD is connected to a trusted full node |
| trust-store | DIRECTORY | "$HOME/.lcd" | false | directory for save checkpoints and validator sets |
Sample command:
```bash
gaiacli light-client --chain-id=test --laddr=tcp://localhost:1317 --node tcp://localhost:46657 --trust-node=false
```
## Gaia Light Use Cases
LCD could be very helpful for related service providers. For a wallet service provider, LCD could
make transaction faster and more reliable in the following cases.
### Create an account
![deposit](pics/create-account.png)
First you need to get a new seed phrase :[get-seed](api.md#keysseed---get)
After having new seed, you could generate a new account with it : [keys](api.md#keys---post)
### Transfer a token
![transfer](pics/transfer-tokens.png)
The first step is to build an asset transfer transaction. Here we can post all necessary parameters
to /create_transfer to get the unsigned transaction byte array. Refer to this link for detailed
operation: [build transaction](api.md#create_transfer---post)
Then sign the returned transaction byte array with users' private key. Finally broadcast the signed
transaction. Refer to this link for how to broadcast the signed transaction: [broadcast transaction](api.md#create_transfer---post)

203
docs/light/load_balancer.md Normal file
View File

@ -0,0 +1,203 @@
# Load Balancing Module - WIP
The LCD will be an important bridge between service providers and cosmos blockchain network. Suppose
a service provider wants to monitor token information for millions of accounts. Then it has to keep
sending a large mount of requests to LCD to query token information. As a result, LCD will send huge
requests to full node to get token information and necessary proof which will cost full node much
computing and bandwidth resource. Too many requests to a single full node may result in some bad
situations:
```text
1. The full node crash possibility increases.
2. The reply delay increases.
3. The system reliability will decrease.
4. As the full node may belong to other people or associates, they may deny too frequent access from a single client.
```
It is very urgent to solve this problems. Here we consider to import load balancing into LCD. By the
help of load balancing, LCD can distribute millions of requests to a set of full nodes. Thus the
load of each full node won't be too heavy and the unavailable full nodes will be wiped out of query
list. In addition, the system reliability will increase.
## Design
This module need combine with client to realize the real load balancing. It can embed the
[HTTP Client](https://github.com/tendermint/tendermint/rpc/lib/client/httpclient.go). In other
wordswe realise the new httpclient based on `HTTP`.
```go
type HTTPLoadBalancer struct {
rpcs map[string]*rpcclient.JSONRPCClient
*WSEvents
}
```
## The Diagram of LCD RPC WorkFlow with LoadBalance
![The Diagram of LCD RPC WorkFlow](pics/loadbalanceDiagram.png)
In the above sequence diagram, application calls the `Request()`, and LCD finally call the
`HTTP.Request()` through the SecureClient `Wrapper`. In every `HTTP.Request()`, `Getclient()`
selects the current working rpcclient by the load balancing algorithm,then run the
`JSONRPCClient.Call()` to request from the Full Node, finally `UpdateClient()` updates the weight of
the current rpcclient according to the status that is returned by the full node. The `GetAddr()`
and `UpdateAddrWeight()` are realized in the load balancing module.
There are some abilities to do:
* Add the Remote Address
* Delete the Remote Address
* Update the weights of the addresses
## Load balancing Strategies
We can design some strategies like nginx to combine the different load balancing algorithms to get
the final remote. We can also get the status of the remote server to add or delete the addresses and
update weights of the addresses.
In a wordit can make the entire LCD work more effective in actual conditions.
We are working this module independently in this [Github Repository](https://github.com/MrXJC/GoLoadBalance).
## Interface And Type
### Balancer
This interface `Balancer`is the core of the package. Every load balancing algorithm should realize
it,and it defined two interfaces.
* `init` initialize the balancer, assigns the variables which `DoBalance` needs.
* `DoBalance` load balance the full node addresses according to the current situation.
```go
package balance
type Balancer interface {
init(NodeAddrs)
DoBalance(NodeAddrs) (*NodeAddr,int,error)
}
```
### NodeAddr
* host: ip address
* port: the number of port
* weight: the weight of this full node address,default:1
This NodeAddr is the base struct of the address.
```go
type NodeAddr struct{
host string
port int
weight int
}
func (p *NodeAddr) GetHost() string
func (p *NodeAddr) GetPort() int
func (p *NodeAddr) GetWeight() int
func (p *NodeAddr) updateWeight(weight int)
```
The `weight` is the important factor that schedules which full node the LCD calls. The weight can be
changed by the information from the full node. So we have the function `updateWegiht`.
### NodeAddrs
>in `balance/types.go`
`NodeAddrs` is the list of the full node address. This is the member variable in the
BalanceManager(`BalancerMgr`).
```go
type NodeAddrs []*NodeAddr
```
## Load Balancing Algorithm
### Random
>in `balance/random.go`
Random algorithm selects a remote address randomly to process the request. The probability of them
being selected is the same.
### RandomWeight
>in `balance/random.go`
RandomWeight Algorithm also selects a remote address randomly to process the request. But the higher
the weight, the greater the probability.
### RoundRobin
>in `balance/roundrobin.go`
RoundRobin Algorithm selects a remote address orderly. Every remote address have the same
probability to be selected.
### RoundRobinWeight
>in `balance/roundrobin.go`
RoundRobinWeight Algorthm selects a remote address orderly. But every remote address have different
probability to be selected which are determined by their weight.
### Hash
//TODO
## Load Balancing Manager
### BalanceMgr
>in `balance/manager.go`
* addrs: the set of the remote full node addresses
* balancers: map the string of balancer name to the specific balancer
* change: record whether the machine reinitialize after the `addrs` changes
`BalanceMgr` is the manager of many balancer. It is the access of load balancing. Its main function
is to maintain the `NodeAddrs` and to call the specific load balancing algorithm above.
```go
type BalanceMgr struct{
addrs NodeAddrs
balancers map[string]Balancer
change map[string]bool
}
func (p *BalanceMgr) RegisterBalancer(name string,balancer Balancer)
func (p *BalanceMgr) updateBalancer(name string)
func (p *BalanceMgr) AddNodeAddr(addr *NodeAddr)
func (p *BalanceMgr) DeleteNodeAddr(i int)
func (p *BalanceMgr) UpdateWeightNodeAddr(i int,weight int)
func (p *BalanceMgr) GetAddr(name string)(*NodeAddr,int,error) {
// if addrs change,update the balancer which we use.
if p.change[name]{
p.updateBalancer(name)
}
// get the balancer by name
balancer := p.balancers[name]
// use the load balancing algorithm
addr,index,err := balancer.DoBalance(p.addrs)
return addr,index,err
}
```
* `RegisterBalancer`: register the basic balancer implementing the `Balancer` interface and initialize them.
* `updateBalancer`: update the specific balancer after the `addrs` change.
* `AddNodeAddr`: add the remote address and set all the values of the `change` to true.
* `DeleteNodeAddr`: delete the remote address and set all the values of the `change` to true.
* `UpdateWeightNodeAddr`: update the weight of the remote address and set all the values of the `change` to true.
* `GetAddr`:select the address by the balancer the `name` decides.

BIN
docs/light/pics/C2H.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/light/pics/H2C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/light/pics/MA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/light/pics/absence1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
docs/light/pics/absence2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/light/pics/absence3.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/light/pics/changeProcess.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/light/pics/deposit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/light/pics/existProof.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/light/pics/substoreProof.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

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