Merge pull request #2739 from cosmos/develop

Release 0.26.0
This commit is contained in:
Jae Kwon 2018-11-08 16:47:38 -08:00 committed by GitHub
commit 8550968c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
178 changed files with 4045 additions and 2007 deletions

View File

@ -137,6 +137,24 @@ jobs:
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make test_sim_gaia_fast make test_sim_gaia_fast
test_sim_gaia_import_export:
<<: *defaults
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
- checkout
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: Test Gaia import/export simulation
command: |
export PATH="$GOBIN:$PATH"
make test_sim_gaia_import_export
test_sim_gaia_multi_seed: test_sim_gaia_multi_seed:
<<: *defaults <<: *defaults
parallelism: 1 parallelism: 1
@ -259,6 +277,9 @@ workflows:
- test_sim_gaia_fast: - test_sim_gaia_fast:
requires: requires:
- setup_dependencies - setup_dependencies
- test_sim_gaia_import_export:
requires:
- setup_dependencies
- test_sim_gaia_multi_seed: - test_sim_gaia_multi_seed:
requires: requires:
- setup_dependencies - setup_dependencies

View File

@ -1,5 +1,62 @@
# Changelog # Changelog
## 0.26.0
BREAKING CHANGES
* Gaia
* [gaiad init] [\#2602](https://github.com/cosmos/cosmos-sdk/issues/2602) New genesis workflow
* SDK
* [simulation] [\#2665](https://github.com/cosmos/cosmos-sdk/issues/2665) only argument to simulation.Invariant is now app
* Tendermint
* Upgrade to version 0.26.0
FEATURES
* Gaia CLI (`gaiacli`)
* [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations
* [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations
* [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on.
* [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction.
* SDK
* [\#1336](https://github.com/cosmos/cosmos-sdk/issues/1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes.
IMPROVEMENTS
* Gaia
* [\#2637](https://github.com/cosmos/cosmos-sdk/issues/2637) [x/gov] Switched inactive and active proposal queues to an iterator based queue
* SDK
* [\#2573](https://github.com/cosmos/cosmos-sdk/issues/2573) [x/distribution] add accum invariance
* [\#2556](https://github.com/cosmos/cosmos-sdk/issues/2556) [x/mock/simulation] Fix debugging output
* [\#2396](https://github.com/cosmos/cosmos-sdk/issues/2396) [x/mock/simulation] Change parameters to get more slashes
* [\#2617](https://github.com/cosmos/cosmos-sdk/issues/2617) [x/mock/simulation] Randomize all genesis parameters
* [\#2669](https://github.com/cosmos/cosmos-sdk/issues/2669) [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store.
* [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) [x/mock/simulation] Use a transition matrix for block size
* [\#2660](https://github.com/cosmos/cosmos-sdk/issues/2660) [x/mock/simulation] Staking transactions get tested far more frequently
* [\#2610](https://github.com/cosmos/cosmos-sdk/issues/2610) [x/stake] Block redelegation to and from the same validator
* [\#2652](https://github.com/cosmos/cosmos-sdk/issues/2652) [x/auth] Add benchmark for get and set account
* [\#2685](https://github.com/cosmos/cosmos-sdk/issues/2685) [store] Add general merkle absence proof (also for empty substores)
* [\#2708](https://github.com/cosmos/cosmos-sdk/issues/2708) [store] Disallow setting nil values
BUG FIXES
* Gaia
* [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators`
* [\#2691](https://github.com/cosmos/cosmos-sdk/issues/2691) Fix local testnet creation by using a single canonical genesis time
- [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators`
- [\#2648](https://github.com/cosmos/cosmos-sdk/issues/2648) [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI
* SDK
* [\#2625](https://github.com/cosmos/cosmos-sdk/issues/2625) [x/gov] fix AppendTag function usage error
* [\#2677](https://github.com/cosmos/cosmos-sdk/issues/2677) [x/stake, x/distribution] various staking/distribution fixes as found by the simulator
* [\#2674](https://github.com/cosmos/cosmos-sdk/issues/2674) [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see [\#2686](https://github.com/cosmos/cosmos-sdk/issues/2686))
* [\#2711](https://github.com/cosmos/cosmos-sdk/issues/2711) [x/stake] Add commission data to `MsgCreateValidator` signature bytes.
* Temporarily disable insecure mode for Gaia Lite
## 0.25.0 ## 0.25.0
*October 24th, 2018* *October 24th, 2018*
@ -752,7 +809,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging)
BREAKING CHANGES BREAKING CHANGES
* [stake] MarshalJSON -> MarshalBinary * [stake] MarshalJSON -> MarshalBinaryLengthPrefixed
* Queries against the store must be prefixed with the path "/store" * Queries against the store must be prefixed with the path "/store"
FEATURES FEATURES

73
Gopkg.lock generated
View File

@ -10,6 +10,7 @@
revision = "48b08affede2cea076a3cf13b2e3f72ed262b743" revision = "48b08affede2cea076a3cf13b2e3f72ed262b743"
[[projects]] [[projects]]
branch = "master"
digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0"
name = "github.com/bartekn/go-bip39" name = "github.com/bartekn/go-bip39"
packages = ["."] packages = ["."]
@ -38,7 +39,7 @@
name = "github.com/btcsuite/btcd" name = "github.com/btcsuite/btcd"
packages = ["btcec"] packages = ["btcec"]
pruneopts = "UT" pruneopts = "UT"
revision = "2a560b2036bee5e3679ec2133eb6520b2f195213" revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0"
[[projects]] [[projects]]
digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2"
@ -62,13 +63,6 @@
revision = "346938d642f2ec3594ed81d874461961cd0faa76" revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0" version = "v1.1.0"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
name = "github.com/ebuchman/fail-test"
packages = ["."]
pruneopts = "UT"
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]] [[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify" name = "github.com/fsnotify/fsnotify"
@ -245,12 +239,12 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3" digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure" name = "github.com/mitchellh/mapstructure"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad" revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.1" version = "v1.1.2"
[[projects]] [[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
@ -296,7 +290,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
name = "github.com/prometheus/common" name = "github.com/prometheus/common"
packages = [ packages = [
"expfmt", "expfmt",
@ -304,11 +298,11 @@
"model", "model",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef" digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
name = "github.com/prometheus/procfs" name = "github.com/prometheus/procfs"
packages = [ packages = [
".", ".",
@ -317,7 +311,7 @@
"xfs", "xfs",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2" revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
[[projects]] [[projects]]
digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64" digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64"
@ -346,12 +340,12 @@
version = "v1.1.2" version = "v1.1.2"
[[projects]] [[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
name = "github.com/spf13/cast" name = "github.com/spf13/cast"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "8965335b8c7107321228e3e3702cab9832751bac" revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
version = "v1.2.0" version = "v1.3.0"
[[projects]] [[projects]]
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
@ -370,12 +364,12 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
name = "github.com/spf13/pflag" name = "github.com/spf13/pflag"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
version = "v1.0.2" version = "v1.0.3"
[[projects]] [[projects]]
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
@ -424,35 +418,23 @@
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
[[projects]] [[projects]]
branch = "master" digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431"
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519",
]
pruneopts = "UT"
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:2c971a45c89ca2ccc735af50919cdee05fbdc54d4bf50625073693300e31ead8"
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12"
version = "v0.12.0" version = "v0.14.0"
[[projects]] [[projects]]
digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430" digest = "1:9f8c4c93658315a795ffd3e0c943d39f78067dd8382b8d7bcfaf6686b92f3978"
name = "github.com/tendermint/iavl" name = "github.com/tendermint/iavl"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "3acc91fb8811db2c5409a855ae1f8e441fe98e2d" revision = "fa74114f764f9827c4ad5573f990ed25bf8c4bac"
version = "v0.11.0" version = "v0.11.1"
[[projects]] [[projects]]
digest = "1:f9c7a1f3ee087476f4883c33cc7c1bdbe56b9670b2fb27855ea2f386393272f5" digest = "1:395820b381043b9d2204e181ddf0f9147397c4a7b8f5dc3162de4cfcddf4589a"
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
packages = [ packages = [
"abci/client", "abci/client",
@ -485,6 +467,7 @@
"libs/db", "libs/db",
"libs/errors", "libs/errors",
"libs/events", "libs/events",
"libs/fail",
"libs/flowrate", "libs/flowrate",
"libs/log", "libs/log",
"libs/pubsub", "libs/pubsub",
@ -505,7 +488,6 @@
"rpc/core", "rpc/core",
"rpc/core/types", "rpc/core/types",
"rpc/grpc", "rpc/grpc",
"rpc/lib",
"rpc/lib/client", "rpc/lib/client",
"rpc/lib/server", "rpc/lib/server",
"rpc/lib/types", "rpc/lib/types",
@ -518,8 +500,8 @@
"version", "version",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778" revision = "03e42d2e3866f01a00625f608e3bbfaeb30690de"
version = "v0.25.1-rc0" version = "v0.26.1-rc0"
[[projects]] [[projects]]
digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666"
@ -530,13 +512,15 @@
version = "v0.1.0" version = "v0.1.0"
[[projects]] [[projects]]
digest = "1:aaff04fa01d9b824fde6799759cc597b3ac3671b9ad31924c28b6557d0ee5284" digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"bcrypt", "bcrypt",
"blowfish", "blowfish",
"chacha20poly1305", "chacha20poly1305",
"curve25519", "curve25519",
"ed25519",
"ed25519/internal/edwards25519",
"hkdf", "hkdf",
"internal/chacha20", "internal/chacha20",
"internal/subtle", "internal/subtle",
@ -705,6 +689,7 @@
"github.com/tendermint/tendermint/rpc/lib/client", "github.com/tendermint/tendermint/rpc/lib/client",
"github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/rpc/lib/server",
"github.com/tendermint/tendermint/types", "github.com/tendermint/tendermint/types",
"github.com/tendermint/tendermint/types/time",
"github.com/tendermint/tendermint/version", "github.com/tendermint/tendermint/version",
"github.com/zondax/ledger-goclient", "github.com/zondax/ledger-goclient",
"golang.org/x/crypto/bcrypt", "golang.org/x/crypto/bcrypt",

View File

@ -1,24 +1,3 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]] [[constraint]]
name = "github.com/bgentry/speakeasy" name = "github.com/bgentry/speakeasy"
version = "~0.1.0" version = "~0.1.0"
@ -49,15 +28,15 @@
[[override]] [[override]]
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
version = "=v0.12.0" version = "v0.14.0"
[[override]] [[override]]
name = "github.com/tendermint/iavl" name = "github.com/tendermint/iavl"
version = "=v0.11.0" version = "=v0.11.1"
[[override]] [[override]]
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
version = "=0.25.1-rc0" version = "v0.26.1-rc0" # TODO replace w/ 0.26.1
## deps without releases: ## deps without releases:
@ -89,7 +68,6 @@
version = "1.0.0" version = "1.0.0"
## transitive deps, without releases: ## transitive deps, without releases:
#
[[override]] [[override]]
name = "github.com/syndtr/goleveldb" name = "github.com/syndtr/goleveldb"
@ -106,4 +84,3 @@
[prune] [prune]
go-tests = true go-tests = true
unused-packages = true unused-packages = true

View File

@ -169,13 +169,22 @@ test_sim_gaia_nondeterminism:
test_sim_gaia_fast: test_sim_gaia_fast:
@echo "Running quick Gaia simulation. This may take several minutes..." @echo "Running quick Gaia simulation. This may take several minutes..."
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=10 -v -timeout 24h
test_sim_gaia_import_export:
@echo "Running Gaia import/export simulation. This may take several minutes..."
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4 -v -timeout 24h
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=11 -v -timeout 24h
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=12 -v -timeout 24h
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=13 -v -timeout 24h
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=414 -v -timeout 24h
@go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4142 -v -timeout 24h
test_sim_gaia_multi_seed: test_sim_gaia_multi_seed:
@echo "Running multi-seed Gaia simulation. This may take awhile!" @echo "Running multi-seed Gaia simulation. This may take awhile!"
@bash scripts/multisim.sh 10 @bash scripts/multisim.sh 25
SIM_NUM_BLOCKS ?= 210 SIM_NUM_BLOCKS ?= 500
SIM_BLOCK_SIZE ?= 200 SIM_BLOCK_SIZE ?= 200
SIM_COMMIT ?= true SIM_COMMIT ?= true
test_sim_gaia_benchmark: test_sim_gaia_benchmark:
@ -250,4 +259,5 @@ localnet-stop:
check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ 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 \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
build-linux build-docker-gaiadnode localnet-start localnet-stop \ build-linux build-docker-gaiadnode localnet-start localnet-stop \
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_multi_seed update_tools update_dev_tools format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \
test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools

View File

@ -337,7 +337,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
} }
// Encode with json // Encode with json
value := codec.Cdc.MustMarshalBinary(result) value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result)
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK), Code: uint32(sdk.ABCICodeOK),
Value: value, Value: value,
@ -394,6 +394,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger). ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
WithMinimumFees(app.minimumFees) WithMinimumFees(app.minimumFees)
// Passes the rest of the path as an argument to the querier. // Passes the rest of the path as an argument to the querier.
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
resBytes, err := querier(ctx, path[2:], req) resBytes, err := querier(ctx, path[2:], req)

View File

@ -358,7 +358,7 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
if len(txBytes) == 0 { if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty") return nil, sdk.ErrTxDecode("txBytes are empty")
} }
err := cdc.UnmarshalBinary(txBytes, &tx) err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
} }
@ -455,7 +455,7 @@ func TestCheckTx(t *testing.T) {
for i := int64(0); i < nTxs; i++ { for i := int64(0); i < nTxs; i++ {
tx := newTxCounter(i, 0) tx := newTxCounter(i, 0)
txBytes, err := codec.MarshalBinary(tx) txBytes, err := codec.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
r := app.CheckTx(txBytes) r := app.CheckTx(txBytes)
assert.True(t, r.IsOK(), fmt.Sprintf("%v", r)) assert.True(t, r.IsOK(), fmt.Sprintf("%v", r))
@ -503,7 +503,7 @@ func TestDeliverTx(t *testing.T) {
for i := 0; i < txPerHeight; i++ { for i := 0; i < txPerHeight; i++ {
counter := int64(blockN*txPerHeight + i) counter := int64(blockN*txPerHeight + i)
tx := newTxCounter(counter, counter) tx := newTxCounter(counter, counter)
txBytes, err := codec.MarshalBinary(tx) txBytes, err := codec.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
res := app.DeliverTx(txBytes) res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
@ -544,7 +544,7 @@ func TestMultiMsgDeliverTx(t *testing.T) {
{ {
app.BeginBlock(abci.RequestBeginBlock{}) app.BeginBlock(abci.RequestBeginBlock{})
tx := newTxCounter(0, 0, 1, 2) tx := newTxCounter(0, 0, 1, 2)
txBytes, err := codec.MarshalBinary(tx) txBytes, err := codec.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
res := app.DeliverTx(txBytes) res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
@ -565,7 +565,7 @@ func TestMultiMsgDeliverTx(t *testing.T) {
tx := newTxCounter(1, 3) tx := newTxCounter(1, 3)
tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{0})
tx.Msgs = append(tx.Msgs, msgCounter2{1}) tx.Msgs = append(tx.Msgs, msgCounter2{1})
txBytes, err := codec.MarshalBinary(tx) txBytes, err := codec.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
res := app.DeliverTx(txBytes) res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
@ -638,7 +638,7 @@ func TestSimulateTx(t *testing.T) {
require.Equal(t, gasConsumed, result.GasUsed) require.Equal(t, gasConsumed, result.GasUsed)
// simulate by calling Query with encoded tx // simulate by calling Query with encoded tx
txBytes, err := cdc.MarshalBinary(tx) txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx)
require.Nil(t, err) require.Nil(t, err)
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: "/app/simulate", Path: "/app/simulate",
@ -648,7 +648,7 @@ func TestSimulateTx(t *testing.T) {
require.True(t, queryResult.IsOK(), queryResult.Log) require.True(t, queryResult.IsOK(), queryResult.Log)
var res sdk.Result var res sdk.Result
codec.Cdc.MustUnmarshalBinary(queryResult.Value, &res) codec.Cdc.MustUnmarshalBinaryLengthPrefixed(queryResult.Value, &res)
require.Nil(t, err, "Result unmarshalling failed") require.Nil(t, err, "Result unmarshalling failed")
require.True(t, res.IsOK(), res.Log) require.True(t, res.IsOK(), res.Log)
require.Equal(t, gasConsumed, res.GasUsed, res.Log) require.Equal(t, gasConsumed, res.GasUsed, res.Log)
@ -729,7 +729,7 @@ func TestRunInvalidTransaction(t *testing.T) {
registerTestCodec(newCdc) registerTestCodec(newCdc)
newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil)
txBytes, err := newCdc.MarshalBinary(tx) txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
res := app.DeliverTx(txBytes) res := app.DeliverTx(txBytes)
require.EqualValues(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), res.Code) require.EqualValues(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), res.Code)

View File

@ -121,8 +121,13 @@ func createVerifier() tmlite.Verifier {
fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String()) fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String())
os.Exit(1) os.Exit(1)
} }
node := rpcclient.NewHTTP(nodeURI, "/websocket") node := rpcclient.NewHTTP(nodeURI, "/websocket")
verifier, err := tmliteProxy.NewVerifier(chainID, filepath.Join(home, ".gaialite"), node, log.NewNopLogger()) cacheSize := 10 // TODO: determine appropriate cache size
verifier, err := tmliteProxy.NewVerifier(
chainID, filepath.Join(home, ".gaialite"),
node, log.NewNopLogger(), cacheSize,
)
if err != nil { if err != nil {
fmt.Printf("Create verifier failed: %s\n", err.Error()) fmt.Printf("Create verifier failed: %s\n", err.Error())

View File

@ -10,9 +10,9 @@ import (
"strings" "strings"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteErr "github.com/tendermint/tendermint/lite/errors"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy" tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
@ -54,7 +54,7 @@ func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sd
return res, err return res, err
} }
ctx.Codec.MustUnmarshalBinary(resRaw, &res) ctx.Codec.MustUnmarshalBinaryLengthPrefixed(resRaw, &res)
return return
} }
@ -158,7 +158,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
opts := rpcclient.ABCIQueryOptions{ opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height, Height: ctx.Height,
Trusted: ctx.TrustNode, Prove: !ctx.TrustNode,
} }
result, err := node.ABCIQueryWithOptions(path, key, opts) result, err := node.ABCIQueryWithOptions(path, key, opts)
@ -198,7 +198,7 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) {
} }
// verifyProof perform response proof verification. // verifyProof perform response proof verification.
func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) error {
if ctx.Verifier == nil { if ctx.Verifier == nil {
return fmt.Errorf("missing valid certifier to verify data from distrusted node") return fmt.Errorf("missing valid certifier to verify data from distrusted node")
} }
@ -209,25 +209,22 @@ func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error {
return err return err
} }
var multiStoreProof store.MultiStoreProof // TODO: Instead of reconstructing, stash on CLIContext field?
cdc := codec.New() prt := store.DefaultProofRuntime()
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof) // TODO: Better convention for path?
storeName, err := parseQueryStorePath(queryPath)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to unmarshalBinary rangeProof") return err
} }
// verify the substore commit hash against trusted appHash kp := merkle.KeyPath{}
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
)
if err != nil {
return errors.Wrap(err, "failed in verifying the proof against appHash")
}
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value)
if err != nil { if err != nil {
return errors.Wrap(err, "failed in the range proof verification") return errors.Wrap(err, "failed to prove merkle proof")
} }
return nil return nil
@ -241,20 +238,40 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([
} }
// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath> // isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
// queryType can be app or store. // queryType must be "store" and subpath must be "key" to require a proof.
func isQueryStoreWithProof(path string) bool { func isQueryStoreWithProof(path string) bool {
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
return false return false
} }
paths := strings.SplitN(path[1:], "/", 3) paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 { switch {
case len(paths) != 3:
return false return false
} case paths[0] != "store":
return false
if store.RequireProof("/" + paths[2]) { case store.RequireProof("/" + paths[2]):
return true return true
} }
return false return false
} }
// parseQueryStorePath expects a format like /store/<storeName>/key.
func parseQueryStorePath(path string) (storeName string, err error) {
if !strings.HasPrefix(path, "/") {
return "", errors.New("expected path to start with /")
}
paths := strings.SplitN(path[1:], "/", 3)
switch {
case len(paths) != 3:
return "", errors.New("expected format like /store/<storeName>/key")
case paths[0] != "store":
return "", errors.New("expected format like /store/<storeName>/key")
case paths[2] != "key":
return "", errors.New("expected format like /store/<storeName>/key")
}
return paths[1], nil
}

View File

@ -96,7 +96,12 @@ func GetKeyBaseFromDirWithWritePerm(rootDir string) (keys.Keybase, error) {
// GetKeyBaseFromDir initializes a read-only keybase at a particular dir. // GetKeyBaseFromDir initializes a read-only keybase at a particular dir.
func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) { func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
return getKeyBaseFromDirWithOpts(rootDir, &opt.Options{ReadOnly: true}) // Disabled because of the inability to create a new keys database directory
// in the instance of when ReadOnly is set to true.
//
// ref: syndtr/goleveldb#240
// return getKeyBaseFromDirWithOpts(rootDir, &opt.Options{ReadOnly: true})
return getKeyBaseFromDirWithOpts(rootDir, nil)
} }
func getKeyBaseFromDirWithOpts(rootDir string, o *opt.Options) (keys.Keybase, error) { func getKeyBaseFromDirWithOpts(rootDir string, o *opt.Options) (keys.Keybase, error) {

View File

@ -38,6 +38,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK
Subject: pkix.Name{ Subject: pkix.Name{
Organization: []string{"Gaia Lite"}, Organization: []string{"Gaia Lite"},
}, },
DNSNames: []string{"localhost"},
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,

View File

@ -17,7 +17,7 @@ func TestGenerateSelfSignedCert(t *testing.T) {
cert, err := x509.ParseCertificate(certBytes) cert, err := x509.ParseCertificate(certBytes)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 2, len(cert.IPAddresses)) require.Equal(t, 2, len(cert.IPAddresses))
require.Equal(t, 1, len(cert.DNSNames)) require.Equal(t, 2, len(cert.DNSNames))
require.True(t, cert.IsCA) require.True(t, cert.IsCA)
} }

View File

@ -158,11 +158,11 @@ func TestNodeStatus(t *testing.T) {
res, body := Request(t, port, "GET", "/node_info", nil) res, body := Request(t, port, "GET", "/node_info", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var nodeInfo p2p.NodeInfo var nodeInfo p2p.DefaultNodeInfo
err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) err := cdc.UnmarshalJSON([]byte(body), &nodeInfo)
require.Nil(t, err, "Couldn't parse node info") require.Nil(t, err, "Couldn't parse node info")
require.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res) require.NotEqual(t, p2p.DefaultNodeInfo{}, nodeInfo, "res: %v", res)
// syncing // syncing
res, body = Request(t, port, "GET", "/syncing", nil) res, body = Request(t, port, "GET", "/syncing", nil)
@ -628,8 +628,8 @@ func TestSubmitProposal(t *testing.T) {
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
var proposalID int64 var proposalID uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal // query proposal
proposal := getProposal(t, port, proposalID) proposal := getProposal(t, port, proposalID)
@ -650,8 +650,8 @@ func TestDeposit(t *testing.T) {
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
var proposalID int64 var proposalID uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal // query proposal
proposal := getProposal(t, port, proposalID) proposal := getProposal(t, port, proposalID)
@ -684,8 +684,8 @@ func TestVote(t *testing.T) {
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
var proposalID int64 var proposalID uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal // query proposal
proposal := getProposal(t, port, proposalID) proposal := getProposal(t, port, proposalID)
@ -732,18 +732,18 @@ func TestProposalsQuery(t *testing.T) {
// Addr1 proposes (and deposits) proposals #1 and #2 // Addr1 proposes (and deposits) proposals #1 and #2
resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID1 int64 var proposalID1 uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID2 int64 var proposalID2 uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 proposes (and deposits) proposals #3 // Addr2 proposes (and deposits) proposals #3
resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5) resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5)
var proposalID3 int64 var proposalID3 uint64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3) cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 deposits on proposals #2 & #3 // Addr2 deposits on proposals #2 & #3
@ -1230,7 +1230,7 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA
// ============= Governance Module ================ // ============= Governance Module ================
func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal { func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var proposal gov.Proposal var proposal gov.Proposal
@ -1239,7 +1239,7 @@ func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal {
return proposal return proposal
} }
func getDeposits(t *testing.T, port string, proposalID int64) []gov.Deposit { func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var deposits []gov.Deposit var deposits []gov.Deposit
@ -1248,7 +1248,7 @@ func getDeposits(t *testing.T, port string, proposalID int64) []gov.Deposit {
return deposits return deposits
} }
func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.AccAddress) gov.Deposit { func getDeposit(t *testing.T, port string, proposalID uint64, depositerAddr sdk.AccAddress) gov.Deposit {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositerAddr), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositerAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var deposit gov.Deposit var deposit gov.Deposit
@ -1257,7 +1257,7 @@ func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.A
return deposit return deposit
} }
func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddress) gov.Vote { func getVote(t *testing.T, port string, proposalID uint64, voterAddr sdk.AccAddress) gov.Vote {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var vote gov.Vote var vote gov.Vote
@ -1266,7 +1266,7 @@ func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddre
return vote return vote
} }
func getVotes(t *testing.T, port string, proposalID int64) []gov.Vote { func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var votes []gov.Vote var votes []gov.Vote
@ -1358,7 +1358,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
return results return results
} }
func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID int64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) {
acc := getAccount(t, port, proposerAddr) acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber() accnum := acc.GetAccountNumber()
@ -1388,7 +1388,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
return results return results
} }
func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID int64) (resultTx ctypes.ResultBroadcastTxCommit) { func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence // get the account to get the sequence
acc := getAccount(t, port, proposerAddr) acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber() accnum := acc.GetAccountNumber()

View File

@ -2,6 +2,7 @@ package lcd
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -12,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
@ -21,7 +23,6 @@ import (
"github.com/rakyll/statik/fs" "github.com/rakyll/statik/fs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmserver "github.com/tendermint/tendermint/rpc/lib/server" tmserver "github.com/tendermint/tendermint/rpc/lib/server"
) )
@ -47,24 +48,38 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
listenAddr := viper.GetString(flagListenAddr) listenAddr := viper.GetString(flagListenAddr)
handler := createHandler(cdc) handler := createHandler(cdc)
registerSwaggerUI(handler) registerSwaggerUI(handler)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
maxOpen := viper.GetInt(flagMaxOpenConnections) maxOpen := viper.GetInt(flagMaxOpenConnections)
sslHosts := viper.GetString(flagSSLHosts) sslHosts := viper.GetString(flagSSLHosts)
certFile := viper.GetString(flagSSLCertFile) certFile := viper.GetString(flagSSLCertFile)
keyFile := viper.GetString(flagSSLKeyFile) keyFile := viper.GetString(flagSSLKeyFile)
cleanupFunc := func() {}
var listener net.Listener var listener net.Listener
var fingerprint string var fingerprint string
server.TrapSignal(func() {
err := listener.Close()
logger.Error("error closing listener", "err", err)
})
var cleanupFunc func()
// TODO: re-enable insecure mode once #2715 has been addressed
if viper.GetBool(flagInsecure) { if viper.GetBool(flagInsecure) {
listener, err = tmserver.StartHTTPServer( fmt.Println(
listenAddr, handler, logger, "Insecure mode is temporarily disabled, please locally generate an " +
tmserver.Config{MaxOpenConnections: maxOpen}, "SSL certificate to test. Support will be re-enabled soon!",
) )
if err != nil { // listener, err = tmserver.StartHTTPServer(
return // listenAddr, handler, logger,
} // tmserver.Config{MaxOpenConnections: maxOpen},
// )
// if err != nil {
// return
// }
} else { } else {
if certFile != "" { if certFile != "" {
// validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460
@ -72,6 +87,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
// cert/key pair is provided, read the fingerprint // cert/key pair is provided, read the fingerprint
fingerprint, err = fingerprintFromFile(certFile) fingerprint, err = fingerprintFromFile(certFile)
if err != nil { if err != nil {
@ -83,12 +99,15 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
cleanupFunc = func() { cleanupFunc = func() {
os.Remove(certFile) os.Remove(certFile)
os.Remove(keyFile) os.Remove(keyFile)
} }
defer cleanupFunc() defer cleanupFunc()
} }
listener, err = tmserver.StartHTTPAndTLSServer( listener, err = tmserver.StartHTTPAndTLSServer(
listenAddr, handler, listenAddr, handler,
certFile, keyFile, certFile, keyFile,
@ -98,16 +117,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil { if err != nil {
return return
} }
logger.Info(fingerprint)
}
logger.Info("REST server started")
// wait forever and cleanup logger.Info(fingerprint)
cmn.TrapSignal(func() { logger.Info("REST server started")
defer cleanupFunc() }
err := listener.Close()
logger.Error("error closing listener", "err", err) // logger.Info("REST server started")
})
return nil return nil
}, },
@ -124,6 +139,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response")
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))

View File

@ -214,6 +214,7 @@ func InitializeTestLCD(
genTxs := []json.RawMessage{} genTxs := []json.RawMessage{}
// append any additional (non-proposing) validators // append any additional (non-proposing) validators
var accs []gapp.GenesisAccount
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
operPrivKey := secp256k1.GenPrivKey() operPrivKey := secp256k1.GenPrivKey()
operAddr := operPrivKey.PubKey().Address() operAddr := operPrivKey.PubKey().Address()
@ -242,9 +243,17 @@ func InitializeTestLCD(
genTxs = append(genTxs, txBytes) genTxs = append(genTxs, txBytes)
valConsPubKeys = append(valConsPubKeys, pubKey) valConsPubKeys = append(valConsPubKeys, pubKey)
valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr))
accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(operAddr))
accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 150)}
accs = append(accs, gapp.NewGenesisAccount(&accAuth))
} }
genesisState, err := gapp.GaiaAppGenState(cdc, genTxs) appGenState := gapp.NewDefaultGenesisState()
appGenState.Accounts = accs
genDoc.AppState, err = cdc.MarshalJSON(appGenState)
require.NoError(t, err)
genesisState, err := gapp.GaiaAppGenState(cdc, *genDoc, genTxs)
require.NoError(t, err) require.NoError(t, err)
// add some tokens to init accounts // add some tokens to init accounts

View File

@ -125,7 +125,7 @@ type Info struct {
func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) {
var tx auth.StdTx var tx auth.StdTx
err := cdc.UnmarshalBinary(txBytes, &tx) err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -65,6 +65,20 @@ func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok
return n, true return n, true
} }
// ParseUint64OrReturnBadRequest converts s to a uint64 value.
func ParseUint64OrReturnBadRequest(w http.ResponseWriter, s string) (n uint64, ok bool) {
var err error
n, err = strconv.ParseUint(s, 10, 64)
if err != nil {
err := fmt.Errorf("'%s' is not a valid uint64", s)
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return n, false
}
return n, true
}
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a // ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a
// default value, defaultIfEmpty, if the string is empty. // default value, defaultIfEmpty, if the string is empty.
func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) { func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) {

View File

@ -123,7 +123,8 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
// Check whether the address is a signer // Check whether the address is a signer
if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) {
fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'\n", name) return signedStdTx, fmt.Errorf(
"The generated transaction's intended signer does not match the given signer: %q", name)
} }
if !offline && txBldr.AccountNumber == 0 { if !offline && txBldr.AccountNumber == 0 {
@ -166,7 +167,7 @@ func adjustGasEstimate(estimate int64, adjustment float64) int64 {
func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) { func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
var simulationResult sdk.Result var simulationResult sdk.Result
if err := cdc.UnmarshalBinary(rawRes, &simulationResult); err != nil { if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil {
return 0, err return 0, err
} }
return simulationResult.GasUsed, nil return simulationResult.GasUsed, nil

View File

@ -12,7 +12,7 @@ import (
func TestParseQueryResponse(t *testing.T) { func TestParseQueryResponse(t *testing.T) {
cdc := app.MakeCodec() cdc := app.MakeCodec()
sdkResBytes := cdc.MustMarshalBinary(sdk.Result{GasUsed: 10}) sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10})
gas, err := parseQueryResponse(cdc, sdkResBytes) gas, err := parseQueryResponse(cdc, sdkResBytes)
assert.Equal(t, gas, int64(10)) assert.Equal(t, gas, int64(10))
assert.Nil(t, err) assert.Nil(t, err)
@ -28,7 +28,7 @@ func TestCalculateGas(t *testing.T) {
if wantErr { if wantErr {
return nil, errors.New("") return nil, errors.New("")
} }
return cdc.MustMarshalBinary(sdk.Result{GasUsed: gasUsed}), nil return cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: gasUsed}), nil
} }
} }
type args struct { type args struct {

View File

@ -109,7 +109,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.cdc, app.cdc,
app.keyParams, app.tkeyParams, app.keyParams, app.tkeyParams,
) )
app.stakeKeeper = stake.NewKeeper( stakeKeeper := stake.NewKeeper(
app.cdc, app.cdc,
app.keyStake, app.tkeyStake, app.keyStake, app.tkeyStake,
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
@ -117,30 +117,32 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
) )
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
app.paramsKeeper.Subspace(mint.DefaultParamspace), app.paramsKeeper.Subspace(mint.DefaultParamspace),
app.stakeKeeper, app.feeCollectionKeeper, &stakeKeeper, app.feeCollectionKeeper,
) )
app.distrKeeper = distr.NewKeeper( app.distrKeeper = distr.NewKeeper(
app.cdc, app.cdc,
app.keyDistr, app.keyDistr,
app.paramsKeeper.Subspace(distr.DefaultParamspace), app.paramsKeeper.Subspace(distr.DefaultParamspace),
app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper, app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper,
app.RegisterCodespace(stake.DefaultCodespace), app.RegisterCodespace(stake.DefaultCodespace),
) )
app.slashingKeeper = slashing.NewKeeper( app.slashingKeeper = slashing.NewKeeper(
app.cdc, app.cdc,
app.keySlashing, app.keySlashing,
app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
app.RegisterCodespace(slashing.DefaultCodespace), app.RegisterCodespace(slashing.DefaultCodespace),
) )
app.govKeeper = gov.NewKeeper( app.govKeeper = gov.NewKeeper(
app.cdc, app.cdc,
app.keyGov, app.keyGov,
app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, app.stakeKeeper, app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper,
app.RegisterCodespace(gov.DefaultCodespace), app.RegisterCodespace(gov.DefaultCodespace),
) )
// register the staking hooks // register the staking hooks
app.stakeKeeper = app.stakeKeeper.WithHooks( // NOTE: stakeKeeper above are passed by reference,
// so that it can be modified like below:
app.stakeKeeper = *stakeKeeper.SetHooks(
NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))
// register message routes // register message routes
@ -208,9 +210,6 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R
tags := gov.EndBlocker(ctx, app.govKeeper) tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
// Add these new validators to the addr -> pubkey map.
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{ return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates, ValidatorUpdates: validatorUpdates,
Tags: tags, Tags: tags,
@ -229,6 +228,10 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// return sdk.ErrGenesisParse("").TraceCause(err, "") // return sdk.ErrGenesisParse("").TraceCause(err, "")
} }
// sort by account number to maintain consistency
sort.Slice(genesisState.Accounts, func(i, j int) bool {
return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber
})
// load the accounts // load the accounts
for _, gacc := range genesisState.Accounts { for _, gacc := range genesisState.Accounts {
acc := gacc.ToAccount() acc := gacc.ToAccount()
@ -242,7 +245,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
panic(err) // TODO find a way to do this w/o panics panic(err) // TODO find a way to do this w/o panics
} }
// load the address to pubkey map // initialize module-specific stores
auth.InitGenesis(ctx, app.feeCollectionKeeper, genesisState.AuthData)
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData)
@ -259,7 +263,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
if err != nil { if err != nil {
panic(err) panic(err)
} }
bz := app.cdc.MustMarshalBinary(tx) bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx)
res := app.BaseApp.DeliverTx(bz) res := app.BaseApp.DeliverTx(bz)
if !res.IsOK() { if !res.IsOK() {
panic(res.Log) panic(res.Log)
@ -268,12 +272,12 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
} }
app.slashingKeeper.AddValidators(ctx, validators)
// sanity check // sanity check
if len(req.Validators) > 0 { if len(req.Validators) > 0 {
if len(req.Validators) != len(validators) { if len(req.Validators) != len(validators) {
panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d) ", len(req.Validators), len(validators))) panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d)",
len(req.Validators), len(validators)))
} }
sort.Sort(abci.ValidatorUpdates(req.Validators)) sort.Sort(abci.ValidatorUpdates(req.Validators))
sort.Sort(abci.ValidatorUpdates(validators)) sort.Sort(abci.ValidatorUpdates(validators))
@ -303,11 +307,12 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
app.accountKeeper.IterateAccounts(ctx, appendAccount) app.accountKeeper.IterateAccounts(ctx, appendAccount)
genState := NewGenesisState( genState := NewGenesisState(
accounts, accounts,
stake.WriteGenesis(ctx, app.stakeKeeper), auth.ExportGenesis(ctx, app.feeCollectionKeeper),
mint.WriteGenesis(ctx, app.mintKeeper), stake.ExportGenesis(ctx, app.stakeKeeper),
distr.WriteGenesis(ctx, app.distrKeeper), mint.ExportGenesis(ctx, app.mintKeeper),
gov.WriteGenesis(ctx, app.govKeeper), distr.ExportGenesis(ctx, app.distrKeeper),
slashing.GenesisState{}, // TODO create write methods gov.ExportGenesis(ctx, app.govKeeper),
slashing.ExportGenesis(ctx, app.slashingKeeper),
) )
appState, err = codec.MarshalJSONIndent(app.cdc, genState) appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil { if err != nil {
@ -334,12 +339,15 @@ var _ sdk.StakingHooks = Hooks{}
// nolint // nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, valAddr) h.dh.OnValidatorCreated(ctx, valAddr)
h.sh.OnValidatorCreated(ctx, valAddr)
} }
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, valAddr) h.dh.OnValidatorModified(ctx, valAddr)
h.sh.OnValidatorModified(ctx, valAddr)
} }
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, valAddr) h.dh.OnValidatorRemoved(ctx, consAddr, valAddr)
h.sh.OnValidatorRemoved(ctx, consAddr, valAddr)
} }
func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBonded(ctx, consAddr, valAddr) h.dh.OnValidatorBonded(ctx, consAddr, valAddr)
@ -355,10 +363,13 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre
} }
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationCreated(ctx, delAddr, valAddr) h.dh.OnDelegationCreated(ctx, delAddr, valAddr)
h.sh.OnDelegationCreated(ctx, delAddr, valAddr)
} }
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr) h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr)
h.sh.OnDelegationSharesModified(ctx, delAddr, valAddr)
} }
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationRemoved(ctx, delAddr, valAddr) h.dh.OnDelegationRemoved(ctx, delAddr, valAddr)
h.sh.OnDelegationRemoved(ctx, delAddr, valAddr)
} }

View File

@ -26,11 +26,13 @@ var (
// bonded tokens given to genesis validators/accounts // bonded tokens given to genesis validators/accounts
freeFermionVal = int64(100) freeFermionVal = int64(100)
freeFermionsAcc = sdk.NewInt(150) freeFermionsAcc = sdk.NewInt(150)
bondDenom = "steak"
) )
// State to Unmarshal // State to Unmarshal
type GenesisState struct { type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"` Accounts []GenesisAccount `json:"accounts"`
AuthData auth.GenesisState `json:"auth"`
StakeData stake.GenesisState `json:"stake"` StakeData stake.GenesisState `json:"stake"`
MintData mint.GenesisState `json:"mint"` MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"` DistrData distr.GenesisState `json:"distr"`
@ -39,11 +41,12 @@ type GenesisState struct {
GenTxs []json.RawMessage `json:"gentxs"` GenTxs []json.RawMessage `json:"gentxs"`
} }
func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState, func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState {
return GenesisState{ return GenesisState{
Accounts: accounts, Accounts: accounts,
AuthData: authData,
StakeData: stakeData, StakeData: stakeData,
MintData: mintData, MintData: mintData,
DistrData: distrData, DistrData: distrData,
@ -52,16 +55,20 @@ func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mi
} }
} }
// GenesisAccount doesn't need pubkey or sequence // nolint
type GenesisAccount struct { type GenesisAccount struct {
Address sdk.AccAddress `json:"address"` Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence_number"`
AccountNumber int64 `json:"account_number"`
} }
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{ return GenesisAccount{
Address: acc.Address, Address: acc.Address,
Coins: acc.Coins, Coins: acc.Coins,
AccountNumber: acc.AccountNumber,
Sequence: acc.Sequence,
} }
} }
@ -69,6 +76,8 @@ func NewGenesisAccountI(acc auth.Account) GenesisAccount {
return GenesisAccount{ return GenesisAccount{
Address: acc.GetAddress(), Address: acc.GetAddress(),
Coins: acc.GetCoins(), Coins: acc.GetCoins(),
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
} }
} }
@ -77,6 +86,8 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
return &auth.BaseAccount{ return &auth.BaseAccount{
Address: ga.Address, Address: ga.Address,
Coins: ga.Coins.Sort(), Coins: ga.Coins.Sort(),
AccountNumber: ga.AccountNumber,
Sequence: ga.Sequence,
} }
} }
@ -90,58 +101,60 @@ func GaiaAppInit() server.AppInit {
// Create the core parameters for genesis initialization for gaia // Create the core parameters for genesis initialization for gaia
// note that the pubkey input is this machines pubkey // note that the pubkey input is this machines pubkey
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) { func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
if len(appGenTxs) == 0 { genesisState GenesisState, err error) {
err = errors.New("must provide at least genesis transaction")
return if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil {
return genesisState, err
} }
// start with the default staking genesis state // if there are no gen txs to be processed, return the default empty state
stakeData := stake.DefaultGenesisState() if len(appGenTxs) == 0 {
slashingData := slashing.DefaultGenesisState() return genesisState, errors.New("there must be at least one genesis tx")
}
// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
stakeData := genesisState.StakeData
for i, genTx := range appGenTxs { for i, genTx := range appGenTxs {
var tx auth.StdTx var tx auth.StdTx
err = cdc.UnmarshalJSON(genTx, &tx) if err := cdc.UnmarshalJSON(genTx, &tx); err != nil {
if err != nil { return genesisState, err
return
} }
msgs := tx.GetMsgs() msgs := tx.GetMsgs()
if len(msgs) != 1 { if len(msgs) != 1 {
err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message") return genesisState, errors.New(
return "must provide genesis StdTx with exactly 1 CreateValidator message")
}
if _, ok := msgs[0].(stake.MsgCreateValidator); !ok {
return genesisState, fmt.Errorf(
"Genesis transaction %v does not contain a MsgCreateValidator", i)
}
} }
msg := msgs[0].(stake.MsgCreateValidator)
for _, acc := range genesisState.Accounts {
// create the genesis account, give'm few steaks and a buncha token with there name // create the genesis account, give'm few steaks and a buncha token with there name
genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) for _, coin := range acc.Coins {
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply if coin.Denom == bondDenom {
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.
Add(sdk.NewDecFromInt(coin.Amount)) // increase the supply
} }
}
}
genesisState.StakeData = stakeData
genesisState.GenTxs = appGenTxs
return genesisState, nil
}
// create the final app state // NewDefaultGenesisState generates the default state for gaia.
genesisState = GenesisState{ func NewDefaultGenesisState() GenesisState {
Accounts: genaccs, return GenesisState{
StakeData: stakeData, Accounts: nil,
StakeData: stake.DefaultGenesisState(),
MintData: mint.DefaultGenesisState(), MintData: mint.DefaultGenesisState(),
DistrData: distr.DefaultGenesisState(), DistrData: distr.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(), GovData: gov.DefaultGenesisState(),
SlashingData: slashingData, SlashingData: slashing.DefaultGenesisState(),
GenTxs: appGenTxs, GenTxs: nil,
} }
return
}
func genesisAccountFromMsgCreateValidator(msg stake.MsgCreateValidator, amount sdk.Int) GenesisAccount {
accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
accAuth.Coins = []sdk.Coin{
{msg.Description.Moniker + "Token", sdk.NewInt(1000)},
{"steak", amount},
}
return NewGenesisAccount(&accAuth)
} }
// GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants // GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants
@ -175,27 +188,43 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) {
} }
// GaiaAppGenState but with JSON // GaiaAppGenState but with JSON
func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { func GaiaAppGenStateJSON(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState json.RawMessage, err error) {
// create the final app state // create the final app state
genesisState, err := GaiaAppGenState(cdc, appGenTxs) genesisState, err := GaiaAppGenState(cdc, genDoc, appGenTxs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
appState, err = codec.MarshalJSONIndent(cdc, genesisState) return codec.MarshalJSONIndent(cdc, genesisState)
return
} }
// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators, // CollectStdTxs processes and validates application's genesis StdTxs and returns
// appGenTxs, and persistent peers required to generate genesis.json. // the list of appGenTxs, and persistent peers required to generate genesis.json.
func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) (
validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) { appGenTxs []auth.StdTx, persistentPeers string, err error) {
var fos []os.FileInfo var fos []os.FileInfo
fos, err = ioutil.ReadDir(genTxsDir) fos, err = ioutil.ReadDir(genTxsDir)
if err != nil { if err != nil {
return return appGenTxs, persistentPeers, err
} }
var addresses []string // prepare a map of all accounts in genesis state to then validate
// against the validators addresses
var appState GenesisState
if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil {
return appGenTxs, persistentPeers, err
}
addrMap := make(map[string]GenesisAccount, len(appState.Accounts))
for i := 0; i < len(appState.Accounts); i++ {
acc := appState.Accounts[i]
strAddr := string(acc.Address)
addrMap[strAddr] = acc
}
// addresses and IPs (and port) validator server info
var addressesIPs []string
for _, fo := range fos { for _, fo := range fos {
filename := filepath.Join(genTxsDir, fo.Name()) filename := filepath.Join(genTxsDir, fo.Name())
if !fo.IsDir() && (filepath.Ext(filename) != ".json") { if !fo.IsDir() && (filepath.Ext(filename) != ".json") {
@ -204,48 +233,55 @@ func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) (
// get the genStdTx // get the genStdTx
var jsonRawTx []byte var jsonRawTx []byte
jsonRawTx, err = ioutil.ReadFile(filename) if jsonRawTx, err = ioutil.ReadFile(filename); err != nil {
if err != nil { return appGenTxs, persistentPeers, err
return
} }
var genStdTx auth.StdTx var genStdTx auth.StdTx
err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx) if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil {
if err != nil { return appGenTxs, persistentPeers, err
return
} }
appGenTxs = append(appGenTxs, genStdTx) appGenTxs = append(appGenTxs, genStdTx)
nodeAddr := genStdTx.GetMemo() // the memo flag is used to store
if len(nodeAddr) == 0 { // the ip and node-id, for example this may be:
err = fmt.Errorf("couldn't find node's address in %s", fo.Name()) // "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656"
return nodeAddrIP := genStdTx.GetMemo()
if len(nodeAddrIP) == 0 {
return appGenTxs, persistentPeers, fmt.Errorf(
"couldn't find node's address and IP in %s", fo.Name())
} }
// genesis transactions must be single-message
msgs := genStdTx.GetMsgs() msgs := genStdTx.GetMsgs()
if len(msgs) != 1 { if len(msgs) != 1 {
err = errors.New("each genesis transaction must provide a single genesis message")
return return appGenTxs, persistentPeers, errors.New(
"each genesis transaction must provide a single genesis message")
} }
// TODO: this could be decoupled from stake.MsgCreateValidator // validate the validator address and funds against the accounts in the state
// TODO: and we likely want to do it for real world Gaia
msg := msgs[0].(stake.MsgCreateValidator) msg := msgs[0].(stake.MsgCreateValidator)
validators = append(validators, tmtypes.GenesisValidator{ addr := string(sdk.AccAddress(msg.ValidatorAddr))
PubKey: msg.PubKey, acc, ok := addrMap[addr]
Power: freeFermionVal, if !ok {
Name: msg.Description.Moniker, return appGenTxs, persistentPeers, fmt.Errorf(
}) "account %v not in genesis.json: %+v", addr, addrMap)
}
if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) {
err = fmt.Errorf("insufficient fund for the delegation: %s < %s",
acc.Coins.AmountOf(msg.Delegation.Denom), msg.Delegation.Amount)
}
// exclude itself from persistent peers // exclude itself from persistent peers
if msg.Description.Moniker != moniker { if msg.Description.Moniker != moniker {
addresses = append(addresses, nodeAddr) addressesIPs = append(addressesIPs, nodeAddrIP)
} }
} }
sort.Strings(addresses) sort.Strings(addressesIPs)
persistentPeers = strings.Join(addresses, ",") persistentPeers = strings.Join(addressesIPs, ",")
return return appGenTxs, persistentPeers, nil
} }
func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount {

View File

@ -1,11 +1,14 @@
package app package app
import ( import (
"encoding/json"
"testing" "testing"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -27,7 +30,8 @@ var (
func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
// start with the default staking genesis state // start with the default staking genesis state
stakeData := stake.DefaultGenesisState() appState := NewDefaultGenesisState()
stakeData := appState.StakeData
genAccs := make([]GenesisAccount, len(genTxs)) genAccs := make([]GenesisAccount, len(genTxs))
for i, genTx := range genTxs { for i, genTx := range genTxs {
@ -35,17 +39,15 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
require.Equal(t, 1, len(msgs)) require.Equal(t, 1, len(msgs))
msg := msgs[0].(stake.MsgCreateValidator) msg := msgs[0].(stake.MsgCreateValidator)
// get genesis flag account information acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) acc.Coins = sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)}
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply genAccs[i] = NewGenesisAccount(&acc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(150)) // increase the supply
} }
// create the final app state // create the final app state
return GenesisState{ appState.Accounts = genAccs
Accounts: genAccs, return appState
StakeData: stakeData,
GovData: gov.DefaultGenesisState(),
}
} }
func TestToAccount(t *testing.T) { func TestToAccount(t *testing.T) {
@ -68,6 +70,19 @@ func TestGaiaAppGenTx(t *testing.T) {
func TestGaiaAppGenState(t *testing.T) { func TestGaiaAppGenState(t *testing.T) {
cdc := MakeCodec() cdc := MakeCodec()
_ = cdc _ = cdc
var genDoc tmtypes.GenesisDoc
// test unmarshalling error
_, err := GaiaAppGenState(cdc, genDoc, []json.RawMessage{})
require.Error(t, err)
appState := makeGenesisState(t, []auth.StdTx{})
genDoc.AppState, err = json.Marshal(appState)
require.NoError(t, err)
// test validation error
_, err = GaiaAppGenState(cdc, genDoc, []json.RawMessage{})
require.Error(t, err)
// TODO test must provide at least genesis transaction // TODO test must provide at least genesis transaction
// TODO test with both one and two genesis transactions: // TODO test with both one and two genesis transactions:
@ -77,7 +92,10 @@ func TestGaiaAppGenState(t *testing.T) {
func makeMsg(name string, pk crypto.PubKey) auth.StdTx { func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
desc := stake.NewDescription(name, "", "", "") desc := stake.NewDescription(name, "", "", "")
comm := stakeTypes.CommissionMsg{} comm := stakeTypes.CommissionMsg{}
msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin("steak", 50), desc, comm) msg := stake.NewMsgCreateValidator(
sdk.ValAddress(pk.Address()), pk,
sdk.NewInt64Coin(bondDenom, 50), desc, comm,
)
return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "") return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "")
} }
@ -106,3 +124,10 @@ func TestGaiaGenesisValidation(t *testing.T) {
err = GaiaValidateGenesisState(genesisState) err = GaiaValidateGenesisState(genesisState)
require.NotNil(t, err) require.NotNil(t, err)
} }
func TestNewDefaultGenesisAccount(t *testing.T) {
addr := secp256k1.GenPrivKeySecp256k1([]byte("")).PubKey().Address()
acc := NewDefaultGenesisAccount(sdk.AccAddress(addr))
require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("fooToken"))
require.Equal(t, sdk.NewInt(150), acc.Coins.AmountOf(bondDenom))
}

View File

@ -4,12 +4,15 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
"os" "os"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -49,42 +52,93 @@ func init() {
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
var genesisAccounts []GenesisAccount var genesisAccounts []GenesisAccount
amt := int64(10000) amount := int64(r.Intn(1e6))
numInitiallyBonded := int64(r.Intn(250))
numAccs := int64(len(accs))
if numInitiallyBonded > numAccs {
numInitiallyBonded = numAccs
}
fmt.Printf("Selected randomly generated parameters for simulated genesis: {amount of steak per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded)
// Randomly generate some genesis accounts // Randomly generate some genesis accounts
for _, acc := range accs { for _, acc := range accs {
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amt)}} coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amount)}}
genesisAccounts = append(genesisAccounts, GenesisAccount{ genesisAccounts = append(genesisAccounts, GenesisAccount{
Address: acc.Address, Address: acc.Address,
Coins: coins, Coins: coins,
}) })
} }
// Default genesis state // Random genesis states
govGenesis := gov.DefaultGenesisState() govGenesis := gov.GenesisState{
stakeGenesis := stake.DefaultGenesisState() StartingProposalID: uint64(r.Intn(100)),
slashingGenesis := slashing.DefaultGenesisState() DepositParams: gov.DepositParams{
MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))},
MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
VotingParams: gov.VotingParams{
VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
TallyParams: gov.TallyParams{
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
GovernancePenalty: sdk.NewDecWithPrec(1, 2),
},
}
fmt.Printf("Selected randomly generated governance parameters: %+v\n", govGenesis)
stakeGenesis := stake.GenesisState{
Pool: stake.InitialPool(),
Params: stake.Params{
UnbondingTime: time.Duration(r.Intn(60*60*24*3*2)) * time.Second,
MaxValidators: uint16(r.Intn(250)),
BondDenom: "steak",
},
}
fmt.Printf("Selected randomly generated staking parameters: %+v\n", stakeGenesis)
slashingGenesis := slashing.GenesisState{
Params: slashing.Params{
MaxEvidenceAge: stakeGenesis.Params.UnbondingTime,
DoubleSignUnbondDuration: time.Duration(r.Intn(60*60*24)) * time.Second,
SignedBlocksWindow: int64(r.Intn(1000)),
DowntimeUnbondDuration: time.Duration(r.Intn(86400)) * time.Second,
MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1),
SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))),
SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))),
},
}
fmt.Printf("Selected randomly generated slashing parameters: %+v\n", slashingGenesis)
mintGenesis := mint.GenesisState{
Minter: mint.Minter{
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
},
Params: mint.Params{
MintDenom: "steak",
InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
InflationMax: sdk.NewDecWithPrec(20, 2),
InflationMin: sdk.NewDecWithPrec(7, 2),
GoalBonded: sdk.NewDecWithPrec(67, 2),
},
}
fmt.Printf("Selected randomly generated minting parameters: %v\n", mintGenesis)
var validators []stake.Validator var validators []stake.Validator
var delegations []stake.Delegation var delegations []stake.Delegation
// XXX Try different numbers of initially bonded validators
numInitiallyBonded := int64(50)
valAddrs := make([]sdk.ValAddress, numInitiallyBonded) valAddrs := make([]sdk.ValAddress, numInitiallyBonded)
for i := 0; i < int(numInitiallyBonded); i++ { for i := 0; i < int(numInitiallyBonded); i++ {
valAddr := sdk.ValAddress(accs[i].Address) valAddr := sdk.ValAddress(accs[i].Address)
valAddrs[i] = valAddr valAddrs[i] = valAddr
validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{}) validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{})
validator.Tokens = sdk.NewDec(amt) validator.Tokens = sdk.NewDec(amount)
validator.DelegatorShares = sdk.NewDec(amt) validator.DelegatorShares = sdk.NewDec(amount)
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0} delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0}
validators = append(validators, validator) validators = append(validators, validator)
delegations = append(delegations, delegation) delegations = append(delegations, delegation)
} }
stakeGenesis.Pool.LooseTokens = sdk.NewDec(amt*250 + (numInitiallyBonded * amt)) stakeGenesis.Pool.LooseTokens = sdk.NewDec((amount * numAccs) + (numInitiallyBonded * amount))
stakeGenesis.Validators = validators stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations stakeGenesis.Bonds = delegations
mintGenesis := mint.DefaultGenesisState()
genesis := GenesisState{ genesis := GenesisState{
Accounts: genesisAccounts, Accounts: genesisAccounts,
@ -113,7 +167,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
{50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)},
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)},
{100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, {100, govsim.SimulateMsgDeposit(app.govKeeper)},
{100, stakesim.SimulateMsgCreateValidator(app.accountKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountKeeper, app.stakeKeeper)},
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)}, {5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
{100, stakesim.SimulateMsgDelegate(app.accountKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgDelegate(app.accountKeeper, app.stakeKeeper)},
@ -141,7 +195,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
var logger log.Logger var logger log.Logger
logger = log.NewNopLogger() logger = log.NewNopLogger()
var db dbm.DB var db dbm.DB
dir := os.TempDir() dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir) db, _ = dbm.NewGoLevelDB("Simulation", dir)
defer func() { defer func() {
db.Close() db.Close()
@ -183,7 +237,13 @@ func TestFullGaiaSimulation(t *testing.T) {
} else { } else {
logger = log.NewNopLogger() logger = log.NewNopLogger()
} }
db := dbm.NewMemDB() var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
app := NewGaiaApp(logger, db, nil) app := NewGaiaApp(logger, db, nil)
require.Equal(t, "GaiaApp", app.Name()) require.Equal(t, "GaiaApp", app.Name())
@ -198,11 +258,112 @@ func TestFullGaiaSimulation(t *testing.T) {
commit, commit,
) )
if commit { if commit {
fmt.Println("Database Size", db.Stats()["database.size"]) // for memdb:
// fmt.Println("Database Size", db.Stats()["database.size"])
fmt.Println("GoLevelDB Stats")
fmt.Println(db.Stats()["leveldb.stats"])
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
} }
require.Nil(t, err) require.Nil(t, err)
} }
func TestGaiaImportExport(t *testing.T) {
if !enabled {
t.Skip("Skipping Gaia import/export simulation")
}
// Setup Gaia application
var logger log.Logger
if verbose {
logger = log.TestingLogger()
} else {
logger = log.NewNopLogger()
}
var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
app := NewGaiaApp(logger, db, nil)
require.Equal(t, "GaiaApp", app.Name())
// Run randomized simulation
err := simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app),
[]simulation.RandSetup{},
invariants(app),
numBlocks,
blockSize,
commit,
)
if commit {
// for memdb:
// fmt.Println("Database Size", db.Stats()["database.size"])
fmt.Println("GoLevelDB Stats")
fmt.Println(db.Stats()["leveldb.stats"])
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
}
require.Nil(t, err)
fmt.Printf("Exporting genesis...\n")
appState, _, err := app.ExportAppStateAndValidators()
if err != nil {
panic(err)
}
fmt.Printf("Importing genesis...\n")
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir)
defer func() {
newDB.Close()
os.RemoveAll(newDir)
}()
newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil)
require.Equal(t, "GaiaApp", newApp.Name())
request := abci.RequestInitChain{
AppStateBytes: appState,
}
newApp.InitChain(request)
newApp.Commit()
fmt.Printf("Comparing stores...\n")
ctxA := app.NewContext(true, abci.Header{})
ctxB := newApp.NewContext(true, abci.Header{})
type StoreKeysPrefixes struct {
A sdk.StoreKey
B sdk.StoreKey
Prefixes [][]byte
}
storeKeysPrefixes := []StoreKeysPrefixes{
{app.keyMain, newApp.keyMain, [][]byte{}},
{app.keyAccount, newApp.keyAccount, [][]byte{}},
{app.keyStake, newApp.keyStake, [][]byte{stake.UnbondingQueueKey, stake.RedelegationQueueKey, stake.ValidatorQueueKey}}, // ordering may change but it doesn't matter
{app.keySlashing, newApp.keySlashing, [][]byte{}},
{app.keyMint, newApp.keyMint, [][]byte{}},
{app.keyDistr, newApp.keyDistr, [][]byte{}},
{app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}},
{app.keyParams, newApp.keyParams, [][]byte{}},
{app.keyGov, newApp.keyGov, [][]byte{}},
}
for _, storeKeysPrefix := range storeKeysPrefixes {
storeKeyA := storeKeysPrefix.A
storeKeyB := storeKeysPrefix.B
prefixes := storeKeysPrefix.Prefixes
storeA := ctxA.KVStore(storeKeyA)
storeB := ctxB.KVStore(storeKeyB)
kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes)
fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB)
require.True(t, equal, "unequal stores: %s / %s:\nstore A %s (%X) => %s (%X)\nstore B %s (%X) => %s (%X)",
storeKeyA, storeKeyB, kvA.Key, kvA.Key, kvA.Value, kvA.Value, kvB.Key, kvB.Key, kvB.Value, kvB.Value)
}
}
// TODO: Make another test for the fuzzer itself, which just has noOp txs // TODO: Make another test for the fuzzer itself, which just has noOp txs
// and doesn't depend on gaia // and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) { func TestAppStateDeterminism(t *testing.T) {

View File

@ -8,8 +8,11 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"testing" "testing"
"github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -207,7 +210,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
func TestGaiaCLICreateValidator(t *testing.T) { func TestGaiaCLICreateValidator(t *testing.T) {
chainID, servAddr, port := initializeFixtures(t) chainID, servAddr, port := initializeFixtures(t)
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) flags := fmt.Sprintf("--home=%s --chain-id=%v --node=%s", gaiacliHome, chainID, servAddr)
// start gaiad server // start gaiad server
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr))
@ -285,6 +288,12 @@ func TestGaiaCLICreateValidator(t *testing.T) {
validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags))
require.Equal(t, "1.0000000000", validator.Tokens.String()) require.Equal(t, "1.0000000000", validator.Tokens.String())
validatorUbds := executeGetValidatorUnbondingDelegations(t,
fmt.Sprintf("gaiacli query unbonding-delegations-from %s --output=json %v",
sdk.ValAddress(barAddr), flags))
require.Len(t, validatorUbds, 1)
require.Equal(t, "1", validatorUbds[0].Balance.Amount.String())
params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags)) params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags))
require.True(t, defaultParams.Equal(params)) require.True(t, defaultParams.Equal(params))
@ -340,7 +349,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64()) require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID()) require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
@ -383,7 +392,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64()) require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID()) require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus()) require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
voteStr := fmt.Sprintf("gaiacli tx vote %v", flags) voteStr := fmt.Sprintf("gaiacli tx vote %v", flags)
@ -405,12 +414,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
tests.WaitForNextNBlocksTM(2, port) tests.WaitForNextNBlocksTM(2, port)
vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags)) vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
require.Equal(t, int64(1), vote.ProposalID) require.Equal(t, uint64(1), vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option) require.Equal(t, gov.OptionYes, vote.Option)
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags)) votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags))
require.Len(t, votes, 1) require.Len(t, votes, 1)
require.Equal(t, int64(1), votes[0].ProposalID) require.Equal(t, uint64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option) require.Equal(t, gov.OptionYes, votes[0].Option)
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "") proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "")
@ -430,7 +439,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
executeWrite(t, spStr, app.DefaultKeyPass) executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port) tests.WaitForNextNBlocksTM(2, port)
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "") proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --limit=1 %v", flags), "")
require.Equal(t, " 2 - Apples", proposalsQuery) require.Equal(t, " 2 - Apples", proposalsQuery)
} }
@ -439,7 +448,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
// start gaiad server // start gaiad server
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf(
"gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr))
defer proc.Stop(false) defer proc.Stop(false)
tests.WaitForTMStart(port) tests.WaitForTMStart(port)
@ -484,11 +494,11 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
unsignedTxFile := writeToNewTempFile(t, stdout) unsignedTxFile := writeToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name()) defer os.Remove(unsignedTxFile.Name())
// Test sign --print-sigs // Test sign --validate-signatures
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name())) "gaiacli tx sign %v --validate-signatures %v", flags, unsignedTxFile.Name()))
require.True(t, success) require.False(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout) require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
// Test sign // Test sign
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
@ -505,15 +515,17 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
// Test sign --print-signatures // Test sign --print-signatures
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli tx sign %v --print-sigs %v", flags, signedTxFile.Name())) "gaiacli tx sign %v --validate-signatures %v", flags, signedTxFile.Name()))
require.True(t, success) require.True(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\n", fooAddr.String(), fooAddr.String()), stdout) require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(),
fooAddr.String()), stdout)
// Test broadcast // Test broadcast
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name()))
require.True(t, success) require.True(t, success)
var result struct { var result struct {
Response abci.ResponseDeliverTx Response abci.ResponseDeliverTx
@ -535,7 +547,7 @@ func TestGaiaCLIConfig(t *testing.T) {
servAddr, port, err := server.FreeTCPAddr() servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err) require.NoError(t, err)
node := fmt.Sprintf("%s:%s", servAddr, port) node := fmt.Sprintf("%s:%s", servAddr, port)
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) chainID := executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome))
executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y") executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y")
config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
require.NoError(t, err) require.NoError(t, err)
@ -585,12 +597,27 @@ func initializeFixtures(t *testing.T) (chainID, servAddr, port string) {
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") 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 foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s foo", 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), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass)
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf(
"gaiacli keys show foo --output=json --home=%s", gaiacliHome))
chainID = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome))
genFile := filepath.Join(gaiadHome, "config", "genesis.json")
genDoc := readGenesisFile(t, genFile)
var appState app.GenesisState
err := codec.Cdc.UnmarshalJSON(genDoc.AppState, &appState)
require.NoError(t, err)
appState.Accounts = []app.GenesisAccount{app.NewDefaultGenesisAccount(fooAddr)}
appStateJSON, err := codec.Cdc.MarshalJSON(appState)
require.NoError(t, err)
genDoc.AppState = appStateJSON
genDoc.SaveAs(genFile)
executeWrite(t, fmt.Sprintf(
"gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome),
app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiad collect-gentxs --home=%s", gaiadHome), app.DefaultKeyPass)
// get a free port, also setup some common flags // get a free port, also setup some common flags
servAddr, port, err := server.FreeTCPAddr() servAddr, port, err = server.FreeTCPAddr()
require.NoError(t, err) require.NoError(t, err)
return return
} }
@ -609,6 +636,18 @@ func writeToNewTempFile(t *testing.T, s string) *os.File {
return fp return fp
} }
func readGenesisFile(t *testing.T, genFile string) types.GenesisDoc {
var genDoc types.GenesisDoc
fp, err := os.Open(genFile)
require.NoError(t, err)
fileContents, err := ioutil.ReadAll(fp)
require.NoError(t, err)
defer fp.Close()
err = codec.Cdc.UnmarshalJSON(fileContents, &genDoc)
require.NoError(t, err)
return genDoc
}
//___________________________________________________________________________________ //___________________________________________________________________________________
// executors // executors
@ -693,6 +732,24 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
return validator return validator
} }
func executeGetValidatorUnbondingDelegations(t *testing.T, cmdStr string) []stake.UnbondingDelegation {
out, _ := tests.ExecuteT(t, cmdStr, "")
var ubds []stake.UnbondingDelegation
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &ubds)
require.NoError(t, err, "out %v\n, err %v", out, err)
return ubds
}
func executeGetValidatorRedelegations(t *testing.T, cmdStr string) []stake.Redelegation {
out, _ := tests.ExecuteT(t, cmdStr, "")
var reds []stake.Redelegation
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &reds)
require.NoError(t, err, "out %v\n, err %v", out, err)
return reds
}
func executeGetPool(t *testing.T, cmdStr string) stake.Pool { func executeGetPool(t *testing.T, cmdStr string) stake.Pool {
out, _ := tests.ExecuteT(t, cmdStr, "") out, _ := tests.ExecuteT(t, cmdStr, "")
var pool stake.Pool var pool stake.Pool

View File

@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
@ -32,6 +33,7 @@ const (
storeGov = "gov" storeGov = "gov"
storeSlashing = "slashing" storeSlashing = "slashing"
storeStake = "stake" storeStake = "stake"
queryRouteStake = "stake"
) )
// rootCmd is the entry point for this binary // rootCmd is the entry point for this binary
@ -46,6 +48,12 @@ func main() {
cobra.EnableCommandSorting = false cobra.EnableCommandSorting = false
cdc := app.MakeCodec() cdc := app.MakeCodec()
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal()
// TODO: setup keybase, viper object, etc. to be passed into // TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc // with the cdc
@ -70,21 +78,23 @@ func main() {
authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)), authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryDelegation(storeStake, cdc), stakecmd.GetCmdQueryDelegation(storeStake, cdc),
stakecmd.GetCmdQueryDelegations(storeStake, cdc), stakecmd.GetCmdQueryDelegations(storeStake, cdc),
stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc),
stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc),
stakecmd.GetCmdQueryRedelegation(storeStake, cdc),
stakecmd.GetCmdQueryRedelegations(storeStake, cdc),
stakecmd.GetCmdQueryValidator(storeStake, cdc),
stakecmd.GetCmdQueryValidators(storeStake, cdc),
stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc),
stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc),
stakecmd.GetCmdQueryParams(storeStake, cdc), stakecmd.GetCmdQueryParams(storeStake, cdc),
stakecmd.GetCmdQueryPool(storeStake, cdc), stakecmd.GetCmdQueryPool(storeStake, cdc),
govcmd.GetCmdQueryProposal(storeGov, cdc), govcmd.GetCmdQueryProposal(storeGov, cdc),
govcmd.GetCmdQueryProposals(storeGov, cdc), govcmd.GetCmdQueryProposals(storeGov, cdc),
govcmd.GetCmdQueryDeposit(storeGov, cdc),
govcmd.GetCmdQueryDeposits(storeGov, cdc),
stakecmd.GetCmdQueryRedelegation(storeStake, cdc),
stakecmd.GetCmdQueryRedelegations(storeStake, cdc),
slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc),
stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc),
stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc),
stakecmd.GetCmdQueryValidator(storeStake, cdc),
stakecmd.GetCmdQueryValidators(storeStake, cdc),
govcmd.GetCmdQueryVote(storeGov, cdc), govcmd.GetCmdQueryVote(storeGov, cdc),
govcmd.GetCmdQueryVotes(storeGov, cdc), govcmd.GetCmdQueryVotes(storeGov, cdc),
govcmd.GetCmdQueryDeposit(storeGov, cdc),
govcmd.GetCmdQueryDeposits(storeGov, cdc),
slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc),
)...) )...)
//Add query commands //Add query commands

View File

@ -18,10 +18,18 @@ import (
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
) )
func main() { func main() {
cdc := app.MakeCodec() cdc := app.MakeCodec()
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal()
ctx := server.NewDefaultContext() ctx := server.NewDefaultContext()
cobra.EnableCommandSorting = false cobra.EnableCommandSorting = false
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{
@ -31,7 +39,8 @@ func main() {
} }
appInit := app.GaiaAppInit() appInit := app.GaiaAppInit()
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit)) rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) rootCmd.AddCommand(gaiaInit.CollectGenTxsCmd(ctx, cdc))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, server.AppInit{}))
rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc))
server.AddCommands(ctx, cdc, rootCmd, appInit, server.AddCommands(ctx, cdc, rootCmd, appInit,

View File

@ -19,6 +19,13 @@ import (
) )
func init() { func init() {
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal()
rootCmd.AddCommand(txCmd) rootCmd.AddCommand(txCmd)
rootCmd.AddCommand(pubkeyCmd) rootCmd.AddCommand(pubkeyCmd)
rootCmd.AddCommand(addrCmd) rootCmd.AddCommand(addrCmd)
@ -213,7 +220,7 @@ func runTxCmd(cmd *cobra.Command, args []string) error {
var tx = auth.StdTx{} var tx = auth.StdTx{}
cdc := gaia.MakeCodec() cdc := gaia.MakeCodec()
err = cdc.UnmarshalBinary(txBytes, &tx) err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return err return err
} }

118
cmd/gaia/init/collect.go Normal file
View File

@ -0,0 +1,118 @@
package init
import (
"encoding/json"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/types"
)
type initConfig struct {
ChainID string
GenTxsDir string
Name string
NodeID string
ValPubKey crypto.PubKey
}
// nolint
func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "collect-gentxs",
Short: "Collect genesis txs and output a genesis.json file",
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName)
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
if err != nil {
return err
}
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil {
return err
}
toPrint := printInfo{
Moniker: config.Moniker,
ChainID: genDoc.ChainID,
NodeID: nodeID,
}
initCfg := initConfig{
ChainID: genDoc.ChainID,
GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"),
Name: name,
NodeID: nodeID,
ValPubKey: valPubKey,
}
appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
if err != nil {
return err
}
toPrint.AppMessage = appMessage
// print out some key information
return displayInfo(cdc, toPrint)
},
}
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
return cmd
}
func genAppStateFromConfig(
cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc,
) (appState json.RawMessage, err error) {
genFile := config.GenesisFile()
var (
appGenTxs []auth.StdTx
persistentPeers string
genTxs []json.RawMessage
jsonRawTx json.RawMessage
)
// process genesis transactions, else create default genesis.json
appGenTxs, persistentPeers, err = app.CollectStdTxs(
cdc, config.Moniker, initCfg.GenTxsDir, genDoc,
)
if err != nil {
return
}
genTxs = make([]json.RawMessage, len(appGenTxs))
config.P2P.PersistentPeers = persistentPeers
for i, stdTx := range appGenTxs {
jsonRawTx, err = cdc.MarshalJSON(stdTx)
if err != nil {
return
}
genTxs[i] = jsonRawTx
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs)
if err != nil {
return
}
err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState)
return
}

View File

@ -55,9 +55,20 @@ following delegation and commission default parameters:
if err != nil { if err != nil {
return err return err
} }
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil {
return err
}
// Read --pubkey, if empty take it from priv_validator.json
if valPubKeyString := viper.GetString(cli.FlagPubKey); valPubKeyString != "" {
valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString)
if err != nil {
return err
}
}
// Run gaiad tx create-validator // Run gaiad tx create-validator
prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey) prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey)
createValidatorCmd := cli.GetCmdCreateValidator(cdc) createValidatorCmd := cli.GetCmdCreateValidator(cdc)
w, err := ioutil.TempFile("", "gentx") w, err := ioutil.TempFile("", "gentx")
@ -82,28 +93,41 @@ following delegation and commission default parameters:
}, },
} }
cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().AddFlagSet(cli.FsCommissionCreate)
cmd.Flags().AddFlagSet(cli.FsAmount)
cmd.Flags().AddFlagSet(cli.FsPk)
cmd.MarkFlagRequired(client.FlagName) cmd.MarkFlagRequired(client.FlagName)
return cmd return cmd
} }
func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip string, valPubKey crypto.PubKey) { func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID string,
valPubKey crypto.PubKey) {
viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home
viper.Set(client.FlagChainID, chainID)
viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from
viper.Set(cli.FlagNodeID, nodeID) // --node-id viper.Set(cli.FlagNodeID, nodeID) // --node-id
viper.Set(cli.FlagIP, ip) // --ip viper.Set(cli.FlagIP, ip) // --ip
viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey
viper.Set(cli.FlagAmount, defaultAmount) // --amount
viper.Set(cli.FlagCommissionRate, defaultCommissionRate)
viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate)
viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate)
viper.Set(cli.FlagGenesisFormat, true) // --genesis-format viper.Set(cli.FlagGenesisFormat, true) // --genesis-format
viper.Set(cli.FlagMoniker, config.Moniker) // --moniker viper.Set(cli.FlagMoniker, config.Moniker) // --moniker
if config.Moniker == "" { if config.Moniker == "" {
viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName))
} }
if viper.GetString(cli.FlagAmount) == "" {
viper.Set(cli.FlagAmount, defaultAmount)
}
if viper.GetString(cli.FlagCommissionRate) == "" {
viper.Set(cli.FlagCommissionRate, defaultCommissionRate)
}
if viper.GetString(cli.FlagCommissionMaxRate) == "" {
viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate)
}
if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" {
viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate)
}
} }
func prepareFlagsForTxSign() { func prepareFlagsForTxSign() {

View File

@ -2,51 +2,27 @@ package init
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/privval"
"os" "os"
"path/filepath" "path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
) )
const ( const (
flagWithTxs = "with-txs"
flagOverwrite = "overwrite" flagOverwrite = "overwrite"
flagClientHome = "home-client" flagClientHome = "home-client"
flagOverwriteKey = "overwrite-key"
flagSkipGenesis = "skip-genesis"
flagMoniker = "moniker" flagMoniker = "moniker"
) )
type initConfig struct {
ChainID string
GenTxsDir string
Name string
NodeID string
ClientHome string
WithTxs bool
Overwrite bool
OverwriteKey bool
ValPubKey crypto.PubKey
}
type printInfo struct { type printInfo struct {
Moniker string `json:"moniker"` Moniker string `json:"moniker"`
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
@ -70,22 +46,16 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
Short: "Initialize private validator, p2p, genesis, and application configuration files", Short: "Initialize private validator, p2p, genesis, and application configuration files",
Long: `Initialize validators's and node's configuration files. Long: `Initialize validators's and node's configuration files.`,
Note that only node's configuration files will be written if the flag --skip-genesis is
enabled, and the genesis file will not be generated.
`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error { RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag)) config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName)
chainID := viper.GetString(client.FlagChainID) chainID := viper.GetString(client.FlagChainID)
if chainID == "" { if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
} }
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) nodeID, _, err := InitializeNodeValidatorFiles(config)
if err != nil { if err != nil {
return err return err
} }
@ -93,37 +63,26 @@ enabled, and the genesis file will not be generated.
if viper.GetString(flagMoniker) != "" { if viper.GetString(flagMoniker) != "" {
config.Moniker = viper.GetString(flagMoniker) config.Moniker = viper.GetString(flagMoniker)
} }
if config.Moniker == "" && name != "" {
config.Moniker = name var appState json.RawMessage
genFile := config.GenesisFile()
if appState, err = initializeEmptyGenesis(cdc, genFile, chainID,
viper.GetBool(flagOverwrite)); err != nil {
return err
} }
if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil {
return err
}
toPrint := printInfo{ toPrint := printInfo{
ChainID: chainID, ChainID: chainID,
Moniker: config.Moniker, Moniker: config.Moniker,
NodeID: nodeID, NodeID: nodeID,
AppMessage: appState,
} }
if viper.GetBool(flagSkipGenesis) {
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return displayInfo(cdc, toPrint)
}
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"),
Name: name,
NodeID: nodeID,
ClientHome: viper.GetString(flagClientHome),
WithTxs: viper.GetBool(flagWithTxs),
Overwrite: viper.GetBool(flagOverwrite),
OverwriteKey: viper.GetBool(flagOverwriteKey),
ValPubKey: valPubKey,
}
appMessage, err := initWithConfig(cdc, config, initCfg)
// print out some key information
if err != nil {
return err
}
toPrint.AppMessage = appMessage
return displayInfo(cdc, toPrint) return displayInfo(cdc, toPrint)
}, },
} }
@ -131,151 +90,6 @@ enabled, and the genesis file will not be generated.
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/") cmd.Flags().String(flagMoniker, "", "set the validator's moniker")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key")
cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json")
return cmd return cmd
} }
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID = string(nodeKey.ID())
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile())
return
}
func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) (
appMessage json.RawMessage, err error) {
genFile := config.GenesisFile()
if !initCfg.Overwrite && common.FileExists(genFile) {
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
return
}
// process genesis transactions, else create default genesis.json
var appGenTxs []auth.StdTx
var persistentPeers string
var genTxs []json.RawMessage
var appState json.RawMessage
var jsonRawTx json.RawMessage
chainID := initCfg.ChainID
if initCfg.WithTxs {
_, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc)
if err != nil {
return
}
genTxs = make([]json.RawMessage, len(appGenTxs))
config.P2P.PersistentPeers = persistentPeers
for i, stdTx := range appGenTxs {
jsonRawTx, err = cdc.MarshalJSON(stdTx)
if err != nil {
return
}
genTxs[i] = jsonRawTx
}
} else {
var ip, keyPass, secret string
var addr sdk.AccAddress
var signedTx auth.StdTx
if initCfg.Name == "" {
err = errors.New("must specify validator's moniker (--name)")
return
}
config.Moniker = initCfg.Name
ip, err = server.ExternalIP()
if err != nil {
return
}
memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip)
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.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
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey)
if err != nil {
return
}
appMessage, err = json.Marshal(map[string]string{"secret": secret})
if err != nil {
return
}
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr),
initCfg.ValPubKey,
sdk.NewInt64Coin("steak", 100),
stake.NewDescription(config.Moniker, "", "", ""),
stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
)
txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID)
signedTx, err = txBldr.SignStdTx(
initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo), false,
)
if err != nil {
return
}
jsonRawTx, err = cdc.MarshalJSON(signedTx)
if err != nil {
return
}
genTxs = []json.RawMessage{jsonRawTx}
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
appState, err = app.GaiaAppGenStateJSON(cdc, genTxs)
if err != nil {
return
}
err = WriteGenesisFile(genFile, chainID, nil, appState)
return
}
// WriteGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
// nolint: unparam
func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genesisFile)
}
// read of create the private key file for this config
func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey {
// private validator
var privValidator *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.GenFilePV(privValFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}

View File

@ -42,7 +42,6 @@ func setupClientHome(t *testing.T) func() {
clientDir, err := ioutil.TempDir("", "mock-sdk-cmd") clientDir, err := ioutil.TempDir("", "mock-sdk-cmd")
require.Nil(t, err) require.Nil(t, err)
viper.Set(flagClientHome, clientDir) viper.Set(flagClientHome, clientDir)
viper.Set(flagOverwriteKey, true)
return func() { return func() {
if err := os.RemoveAll(clientDir); err != nil { if err := os.RemoveAll(clientDir); err != nil {
// TODO: Handle with #870 // TODO: Handle with #870

View File

@ -3,6 +3,10 @@ package init
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
@ -10,9 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -20,22 +21,25 @@ import (
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
) )
var ( var (
nodeDirPrefix = "node-dir-prefix" flagNodeDirPrefix = "node-dir-prefix"
nValidators = "v" flagNumValidators = "v"
outputDir = "output-dir" flagOutputDir = "output-dir"
nodeDaemonHome = "node-daemon-home" flagNodeDaemonHome = "node-daemon-home"
nodeCliHome = "node-cli-home" flagNodeCliHome = "node-cli-home"
flagStartingIPAddress = "starting-ip-address"
startingIPAddress = "starting-ip-address"
) )
const nodeDirPerm = 0755 const nodeDirPerm = 0755
// get cmd to initialize all files for tendermint testnet and application // get cmd to initialize all files for tendermint testnet and application
func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec,
appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "testnet", Use: "testnet",
Short: "Initialize files for a Gaiad testnet", Short: "Initialize files for a Gaiad testnet",
@ -45,48 +49,59 @@ necessary files (private validator, genesis, config, etc.).
Note, strict routability for addresses is turned off in the config file. Note, strict routability for addresses is turned off in the config file.
Example: Example:
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2
`, `,
RunE: func(_ *cobra.Command, _ []string) error { RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config config := ctx.Config
return testnetWithConfig(config, cdc, appInit) return initTestnet(config, cdc)
}, },
} }
cmd.Flags().Int(nValidators, 4,
"Number of validators to initialize the testnet with")
cmd.Flags().StringP(outputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet")
cmd.Flags().String(nodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)")
cmd.Flags().String(nodeDaemonHome, "gaiad",
"Home directory of the node's daemon configuration")
cmd.Flags().String(nodeCliHome, "gaiacli",
"Home directory of the node's cli configuration")
cmd.Flags().String(startingIPAddress, "192.168.0.1", cmd.Flags().Int(flagNumValidators, 4,
"Number of validators to initialize the testnet with",
)
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet",
)
cmd.Flags().String(flagNodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)",
)
cmd.Flags().String(flagNodeDaemonHome, "gaiad",
"Home directory of the node's daemon configuration",
)
cmd.Flags().String(flagNodeCliHome, "gaiacli",
"Home directory of the node's cli configuration",
)
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1",
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
return cmd return cmd
} }
func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppInit) error { func initTestnet(config *cfg.Config, cdc *codec.Codec) error {
outDir := viper.GetString(outputDir) outDir := viper.GetString(flagOutputDir)
numValidators := viper.GetInt(nValidators) numValidators := viper.GetInt(flagNumValidators)
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6) chainID := "chain-" + cmn.RandStr(6)
monikers := make([]string, numValidators) monikers := make([]string, numValidators)
nodeIDs := make([]string, numValidators) nodeIDs := make([]string, numValidators)
valPubKeys := make([]crypto.PubKey, numValidators) valPubKeys := make([]crypto.PubKey, numValidators)
// Generate private key, node ID, initial transaction var (
accs []app.GenesisAccount
genFiles []string
)
// generate private keys, node IDs, and initial transactions
for i := 0; i < numValidators; i++ { for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome) nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome)
nodeCliHomeName := viper.GetString(nodeCliHome) nodeCliHomeName := viper.GetString(flagNodeCliHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName) clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
gentxsDir := filepath.Join(outDir, "gentxs") gentxsDir := filepath.Join(outDir, "gentxs")
config.SetRoot(nodeDir) config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
@ -103,20 +118,27 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
monikers = append(monikers, nodeDirName) monikers = append(monikers, nodeDirName)
config.Moniker = nodeDirName config.Moniker = nodeDirName
ip, err := getIP(i)
ip, err := getIP(i, viper.GetString(flagStartingIPAddress))
if err != nil { if err != nil {
_ = os.RemoveAll(outDir) _ = os.RemoveAll(outDir)
return err return err
} }
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config) nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
if err != nil { if err != nil {
_ = os.RemoveAll(outDir) _ = os.RemoveAll(outDir)
return err return err
} }
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())
buf := client.BufferStdin() buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) prompt := fmt.Sprintf(
"Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass,
)
keyPass, err := client.GetPassword(prompt, buf) keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" { if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from // An error was returned that either failed to read the password from
@ -124,6 +146,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
// length requirements. // length requirements.
return err return err
} }
if keyPass == "" { if keyPass == "" {
keyPass = app.DefaultKeyPass keyPass = app.DefaultKeyPass
} }
@ -133,17 +156,28 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
_ = os.RemoveAll(outDir) _ = os.RemoveAll(outDir)
return err return err
} }
info := map[string]string{"secret": secret} info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info) cliPrint, err := json.Marshal(info)
if err != nil { if err != nil {
return err return err
} }
// Save private key seed words
// save private key seed words
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint) err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil { if err != nil {
return err return err
} }
accs = append(accs, app.GenesisAccount{
Address: addr,
Coins: sdk.Coins{
sdk.NewInt64Coin(fmt.Sprintf("%sToken", nodeDirName), 1000),
sdk.NewInt64Coin("steak", 150),
},
})
msg := stake.NewMsgCreateValidator( msg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr), sdk.ValAddress(addr),
valPubKeys[i], valPubKeys[i],
@ -153,6 +187,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
) )
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo) tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo) txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo)
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false) signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
if err != nil { if err != nil {
_ = os.RemoveAll(outDir) _ = os.RemoveAll(outDir)
@ -165,7 +200,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
return err return err
} }
// Gather gentxs folder // gather gentxs folder
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes) err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil { if err != nil {
_ = os.RemoveAll(outDir) _ = os.RemoveAll(outDir)
@ -173,64 +208,140 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
} }
} }
for i := 0; i < numValidators; i++ { if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil {
return err
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
// Run `init` and generate genesis.json and config.toml
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: gentxsDir,
Name: moniker,
WithTxs: true,
Overwrite: true,
OverwriteKey: false,
NodeID: nodeID,
ValPubKey: valPubKey,
} }
if _, err := initWithConfig(cdc, config, initCfg); err != nil {
err := collectGenFiles(
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome),
)
if err != nil {
return err
}
fmt.Printf("Successfully initialized %d node directories\n", numValidators)
return nil
}
func initGenFiles(
cdc *codec.Codec, chainID string, accs []app.GenesisAccount,
genFiles []string, numValidators int,
) error {
appGenState := app.NewDefaultGenesisState()
appGenState.Accounts = accs
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
if err != nil {
return err
}
genDoc := types.GenesisDoc{
ChainID: chainID,
AppState: appGenStateJSON,
Validators: nil,
}
// generate empty genesis files for each validator and save
for i := 0; i < numValidators; i++ {
if err := genDoc.SaveAs(genFiles[i]); err != nil {
return err return err
} }
} }
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
return nil return nil
} }
func getIP(i int) (ip string, err error) { func collectGenFiles(
ip = viper.GetString(startingIPAddress) cdc *codec.Codec, config *cfg.Config, chainID string,
if len(ip) == 0 { monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string,
) error {
var appState json.RawMessage
genTime := tmtime.Now()
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: gentxsDir,
Name: moniker,
NodeID: nodeID,
ValPubKey: valPubKey,
}
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil {
return err
}
nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
genFile := config.GenesisFile()
// overwrite each validator's genesis file to have a canonical genesis time
err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
if err != nil {
return err
}
}
return nil
}
func getIP(i int, startingIPAddr string) (string, error) {
var (
ip string
err error
)
if len(startingIPAddr) == 0 {
ip, err = server.ExternalIP() ip, err = server.ExternalIP()
if err != nil { if err != nil {
return "", err return "", err
} }
} else { } else {
ip, err = calculateIP(ip, i) ip, err = calculateIP(startingIPAddr, i)
if err != nil { if err != nil {
return "", err return "", err
} }
} }
return ip, nil return ip, nil
} }
func writeFile(name string, dir string, contents []byte) error { func writeFile(name string, dir string, contents []byte) error {
writePath := filepath.Join(dir) writePath := filepath.Join(dir)
file := filepath.Join(writePath, name) file := filepath.Join(writePath, name)
err := cmn.EnsureDir(writePath, 0700) err := cmn.EnsureDir(writePath, 0700)
if err != nil { if err != nil {
return err return err
} }
err = cmn.WriteFile(file, contents, 0600) err = cmn.WriteFile(file, contents, 0600)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
@ -243,5 +354,6 @@ func calculateIP(ip string, i int) (string, error) {
for j := 0; j < i; j++ { for j := 0; j < i; j++ {
ipv4[3]++ ipv4[3]++
} }
return ipv4.String(), nil return ipv4.String(), nil
} }

112
cmd/gaia/init/utils.go Normal file
View File

@ -0,0 +1,112 @@
package init
import (
"encoding/json"
"fmt"
"io/ioutil"
"time"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
amino "github.com/tendermint/go-amino"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
)
// ExportGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
func ExportGenesisFile(
genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage,
) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genFile)
}
// ExportGenesisFileWithTime creates and writes the genesis configuration to disk.
// An error is returned if building or writing the configuration to file fails.
func ExportGenesisFileWithTime(
genFile, chainID string, validators []types.GenesisValidator,
appState json.RawMessage, genTime time.Time,
) error {
genDoc := types.GenesisDoc{
GenesisTime: genTime,
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genFile)
}
// read of create the private key file for this config
func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey {
var privValidator *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.GenFilePV(privValFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(
config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error,
) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return nodeID, valPubKey, err
}
nodeID = string(nodeKey.ID())
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile())
return nodeID, valPubKey, nil
}
func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) {
genContents, err := ioutil.ReadFile(genFile)
if err != nil {
return genDoc, err
}
if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil {
return genDoc, err
}
return genDoc, err
}
func initializeEmptyGenesis(
cdc *codec.Codec, genFile, chainID string, overwrite bool,
) (appState json.RawMessage, err error) {
if !overwrite && common.FileExists(genFile) {
return nil, fmt.Errorf("genesis.json file already exists: %v", genFile)
}
return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState())
}

View File

@ -273,7 +273,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cdc.MustUnmarshalBinary([]byte(signed), sig) cdc.MustUnmarshalBinaryLengthPrefixed([]byte(signed), sig)
return sig, linfo.GetPubKey(), nil return sig, linfo.GetPubKey(), nil
} }
sig, err = priv.Sign(msg) sig, err = priv.Sign(msg)

View File

@ -182,11 +182,11 @@ func (i offlineInfo) GetAddress() types.AccAddress {
// encoding info // encoding info
func writeInfo(i Info) []byte { func writeInfo(i Info) []byte {
return cdc.MustMarshalBinary(i) return cdc.MustMarshalBinaryLengthPrefixed(i)
} }
// decoding info // decoding info
func readInfo(bz []byte) (info Info, err error) { func readInfo(bz []byte) (info Info, err error) {
err = cdc.UnmarshalBinary(bz, &info) err = cdc.UnmarshalBinaryLengthPrefixed(bz, &info)
return return
} }

View File

@ -72,8 +72,8 @@ module.exports = {
title: "Light Client", title: "Light Client",
collapsable: false, collapsable: false,
children: [ children: [
"/light/", "/lite/",
"/light/getting_started" "/lite/getting_started"
] ]
}, },
{ {

View File

@ -36,6 +36,8 @@
- Tags [#1780](https://github.com/cosmos/cosmos-sdk/issues/1780) - Tags [#1780](https://github.com/cosmos/cosmos-sdk/issues/1780)
# Lower priority # Lower priority
- Create some diagrams (see `docs/resources/diagrams/todo.md`)
## Governance v2 ## Governance v2
- Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926 - Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926

View File

@ -1,86 +0,0 @@
module.exports = {
title: "Cosmos Network",
description: "Documentation for the Cosmos Network.",
dest: "./dist/docs",
base: "/docs/",
markdown: {
lineNumbers: true
},
themeConfig: {
lastUpdated: "Last Updated",
nav: [{ text: "Back to Cosmos", link: "https://cosmos.network" }],
sidebar: [
{
title: "Introduction",
collapsable: false,
children: [
"/introduction/cosmos-hub",
"/introduction/tendermint",
]
},
{
title: "Getting Started",
collapsable: false,
children: [
"/getting-started/voyager",
"/getting-started/installation",
"/getting-started/full-node",
"/getting-started/create-testnet"
]
},
{
title: "Cosmos SDK",
collapsable: false,
children: [
["/sdk/overview", "Overview"],
["/sdk/core/intro", "Core"],
"/sdk/core/app1",
"/sdk/core/app2",
"/sdk/core/app3",
"/sdk/core/app4",
"/sdk/core/app5",
// "/sdk/modules",
"/sdk/clients"
]
},
// {
// title: "Specifications",
// collapsable: false,
// children: [
// ["/specs/overview", "Overview"],
// "/specs/governance",
// "/specs/ibc",
// "/specs/staking",
// "/specs/icts",
// ]
// },
{
title: "Lotion JS",
collapsable: false,
children: [["/lotion/overview", "Overview"], "/lotion/building-an-app"]
},
{
title: "Validators",
collapsable: false,
children: [
["/validators/overview", "Overview"],
["/validators/security", "Security"],
["/validators/validator-setup", "Validator Setup"],
"/validators/validator-faq"
]
},
{
title: "Resources",
collapsable: false,
children: [
// ["/resources/faq" "General"],
"/resources/delegator-faq",
["/resources/whitepaper", "Whitepaper - English"],
["/resources/whitepaper-ko", "Whitepaper - 한국어"],
["/resources/whitepaper-zh-CN", "Whitepaper - 中文"],
["/resources/whitepaper-pt", "Whitepaper - Português"]
]
}
]
}
}

View File

@ -0,0 +1,17 @@
The following diagrams should be created to aid in comprehension of the SDK:
- Genesis circuit
- App structure (aka use of baseapp in something like gaia)
- Simulation framework
- Slashing Mechanism
- Staking Mechanism
- Staking/Slashing Mechanism specific to use of hooks
- Governance Mechanism
- Distribution Mechanism
- Inflation Mechanism (easier)
- IBC Mechanism
These diagrams should reference specific structs/interfaces from the codebase,
logic flow and interconnectivity with other mechanisms etc. It's recommended that
https://www.draw.io/ be used, hence the raw diagram xml can be saved directly to
the Cosmos-SDK repo and adapted with the codebase.

View File

@ -181,6 +181,12 @@ gaiacli tx sign \
unsignedSendTx.json > signedSendTx.json unsignedSendTx.json > signedSendTx.json
``` ```
You can validate the transaction's signagures by typing the following:
```bash
gaiacli tx sign --validate-signatures signedSendTx.json
```
You can broadcast the signed transaction to a node by providing the JSON file to the following command: You can broadcast the signed transaction to a node by providing the JSON file to the following command:
``` ```
@ -285,7 +291,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va
gaiacli query unbonding-delegations <account_cosmos> gaiacli query unbonding-delegations <account_cosmos>
``` ```
You can also get previous unbonding-delegation(s) status by adding the `--height` flag. Additionally, as you can get all the unbonding-delegations from a particular validator:
```bash
gaiacli query unbonding-delegations-from <account_cosmosval>
```
To get previous unbonding-delegation(s) status on past blocks, try adding the `--height` flag.
#### Redelegate Tokens #### Redelegate Tokens
@ -321,7 +333,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va
gaiacli query redelegations <account_cosmos> gaiacli query redelegations <account_cosmos>
``` ```
You can also get previous redelegation(s) status by adding the `--height` flag. Additionally, as you can get all the outgoing redelegations from a particular validator:
```bash
gaiacli query redelegations-from <account_cosmosval>
```
To get previous redelegation(s) status on past blocks, try adding the `--height` flag.
### Governance ### Governance

View File

@ -178,7 +178,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg {
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) { return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app2Tx var tx app2Tx
err := cdc.UnmarshalBinary(txBytes, &tx) err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return nil, sdk.ErrTxDecode(err.Error()) return nil, sdk.ErrTxDecode(err.Error())
} }

View File

@ -201,7 +201,7 @@ func (tx app2Tx) GetSignature() []byte {
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) { return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app2Tx var tx app2Tx
err := cdc.UnmarshalBinary(txBytes, &tx) err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return nil, sdk.ErrTxDecode(err.Error()) return nil, sdk.ErrTxDecode(err.Error())
} }

View File

@ -39,7 +39,7 @@ func TestEncoding(t *testing.T) {
cdc := NewCodec() cdc := NewCodec()
testTxDecoder := tx2Decoder(cdc) testTxDecoder := tx2Decoder(cdc)
encodedSendTx, err := cdc.MarshalBinary(sendTxBefore) encodedSendTx, err := cdc.MarshalBinaryLengthPrefixed(sendTxBefore)
require.Nil(t, err, "Error encoding sendTx") require.Nil(t, err, "Error encoding sendTx")
@ -69,7 +69,7 @@ func TestEncoding(t *testing.T) {
Signature: sig, Signature: sig,
} }
encodedIssueTx, err2 := cdc.MarshalBinary(issueTxBefore) encodedIssueTx, err2 := cdc.MarshalBinaryLengthPrefixed(issueTxBefore)
require.Nil(t, err2, "Error encoding issueTx") require.Nil(t, err2, "Error encoding issueTx")

View File

@ -1,159 +1,469 @@
## Vesting # Vesting
### Intro and Requirements <!-- TOC -->
- [Vesting](#vesting)
- [Intro and Requirements](#intro-and-requirements)
- [Vesting Account Types](#vesting-account-types)
- [Vesting Account Specification](#vesting-account-specification)
- [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts)
- [Continuously Vesting Accounts](#continuously-vesting-accounts)
- [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts)
- [Transferring/Sending](#transferringsending)
- [Continuously Vesting Accounts](#continuously-vesting-accounts-1)
- [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-1)
- [Keepers/Handlers](#keepershandlers)
- [Delegating](#delegating)
- [Continuously Vesting Accounts](#continuously-vesting-accounts-2)
- [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-2)
- [Keepers/Handlers](#keepershandlers-1)
- [Undelegating](#undelegating)
- [Continuously Vesting Accounts](#continuously-vesting-accounts-3)
- [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-3)
- [Keepers/Handlers](#keepershandlers-2)
- [Keepers & Handlers](#keepers--handlers)
- [Initializing at Genesis](#initializing-at-genesis)
- [Examples](#examples)
- [Simple](#simple)
- [Slashing](#slashing)
- [Glossary](#glossary)
<!-- /TOC -->
## Intro and Requirements
This paper specifies vesting account implementation for the Cosmos Hub. This paper specifies vesting account implementation for the Cosmos Hub.
The requirements for this vesting account is that it should be initialized during genesis with The requirements for this vesting account is that it should be initialized
a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators during genesis with a starting balance `X` coins and a vesting end time `T`.
and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked.
The vesting account should also be able to spend any coins it receives from other users.
Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their
unlocked coin amount.
### Implementation The owner of this account should be able to delegate to validators
and vote with locked coins, however they cannot send locked coins to other
accounts until those coins have been unlocked. When it comes to governance, it
is yet undefined if we want to allow a vesting account to be able to deposit
vesting coins into proposals.
##### Vesting Account implementation In addition, a vesting account vests all of its coin denominations at the same
rate. This may be subject to change.
NOTE: `Now = ctx.BlockHeader().Time` **Note**: A vesting account could have some vesting and non-vesting coins. To
support such a feature, the `GenesisAccount` type will need to be updated in
order to make such a distinction.
## Vesting Account Types
```go ```go
// VestingAccount defines an interface that any vesting account type must
// implement.
type VestingAccount interface { type VestingAccount interface {
Account Account
AssertIsVestingAccount() // existence implies that account is vesting. AssertIsVestingAccount() // existence implies that account is vesting
// Calculates amount of coins that can be sent to other accounts given the current time // Calculates the amount of coins that can be sent to other accounts given
SendableCoins(sdk.Context) sdk.Coins // the current time.
SpendableCoins(Context) Coins
// Performs delegation accounting.
TrackDelegation(amount)
// Performs undelegation accounting.
TrackUndelegation(amount)
} }
// Implements Vesting Account // BaseVestingAccount implements the VestingAccount interface. It contains all
// Continuously vests by unlocking coins linearly with respect to time // the necessary fields needed for any vesting account implementation.
type BaseVestingAccount struct {
BaseAccount
OriginalVesting Coins // coins in account upon initialization
DelegatedFree Coins // coins that are vested and delegated
EndTime Time // when the coins become unlocked
}
// ContinuousVestingAccount implements the VestingAccount interface. It
// continuously vests by unlocking coins linearly with respect to time.
type ContinuousVestingAccount struct { type ContinuousVestingAccount struct {
BaseAccount BaseAccount
OriginalVestingCoins sdk.Coins // Coins in account on Initialization BaseVestingAccount
ReceivedCoins sdk.Coins // Coins received from other accounts
SentCoins sdk.Coins // Coins sent to other accounts
// StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point DelegatedVesting Coins // coins that vesting and delegated
StartTime time.Time StartTime Time // when the coins start to vest
EndTime time.Time
} }
// Uses time in context to calculate total unlocked coins // DelayedVestingAccount implements the VestingAccount interface. It vests all
SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: // coins after a specific time, but non prior. In other words, it keeps them
// locked until a specified time.
// Coins unlocked by vesting schedule type DelayedVestingAccount struct {
unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime) BaseAccount
BaseVestingAccount
// Must still check for currentCoins constraint since some unlocked coins may have been delegated. }
currentCoins := vacc.BaseAccount.GetCoins()
// min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins
return min(unlockedCoins, currentCoins)
``` ```
The `VestingAccount` interface is used to assert that an account is a vesting account like so: ## Vesting Account Specification
Given a vesting account, we define the following in the proceeding operations:
- `OV`: The original vesting coin amount. It is a constant value.
- `V`: The number of `OV` coins that are still _vesting_. It is derived by `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis.
- `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is computed on demand and not a per-block basis.
- `DV`: The number of delegated _vesting_ coins. It is a variable value. It is stored and modified directly in the vesting account.
- `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable value. It is stored and modified directly in the vesting account.
- `BC`: The number of `OV` coins less any coins that are transferred, which can be negative, or delegated (`DV + DF`). It is considered to be balance of the embedded base account. It is stored and modified directly in the vesting account.
### Determining Vesting & Vested Amounts
It is important to note that these values are computed on demand and not on a
mandatory per-block basis.
#### Continuously Vesting Accounts
To determine the amount of coins that are vested for a given block `B`, the
following is performed:
1. Compute `X := B.Time - StartTime`
2. Compute `Y := EndTime - StartTime`
3. Compute `V' := OV * (X / Y)`
4. Compute `V := OV - V'`
Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`,
is _vesting_.
```go ```go
vacc, ok := acc.(VestingAccount); ok func (cva ContinuousVestingAccount) GetVestedCoins(b Block) Coins {
// We must handle the case where the start time for a vesting account has
// been set into the future or when the start of the chain is not exactly
// known.
if b.Time < va.StartTime {
return ZeroCoins
}
x := b.Time - cva.StartTime
y := cva.EndTime - cva.StartTime
return cva.OriginalVesting * (x / y)
}
func (cva ContinuousVestingAccount) GetVestingCoins(b Block) Coins {
return cva.OriginalVesting - cva.GetVestedCoins(b)
}
``` ```
as well as to calculate the SendableCoins at any given moment. #### Delayed/Discrete Vesting Accounts
The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`, Delayed vesting accounts are easier to reason about as they only have the full
`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. amount vesting up until a certain time, then they all become vested (unlocked).
Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements
the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of
both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period.
##### Changes to Keepers/Handler
Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be
handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like
`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above.
```go ```go
if acc is VestingAccount and Now < vestingAccount.EndTime: func (dva DelayedVestingAccount) GetVestedCoins(b Block) Coins {
// Check if amount is less than currently allowed sendable coins if b.Time >= dva.EndTime {
if msg.Amount > vestingAccount.SendableCoins(ctx) then fail return dva.OriginalVesting
else: }
vestingAccount.SentCoins += msg.Amount
else: return ZeroCoins
// Account has fully vested, treat like regular account }
if msg.Amount > account.GetCoins() then fail
// All checks passed, send the coins
SendCoins(inputs, outputs)
func (dva DelayedVestingAccount) GetVestingCoins(b Block) Coins {
return cva.OriginalVesting - cva.GetVestedCoins(b)
}
``` ```
Coins that are sent to a vesting account after initialization by users sending them coins should be spendable ### Transferring/Sending
immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not
originally own should increment `ReceivedCoins` by the amount sent.
Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute.
CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`. #### Continuously Vesting Accounts
However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented.
### Initializing at Genesis At any given time, a continuous vesting account may transfer: `min((BC + DV) - V, BC)`.
To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be In other words, a vesting account may transfer the minimum of the base account
BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts balance and the base account balance plus the number of currently delegated
as appropriate. vesting coins less the number of coins vested so far.
```go
func (cva ContinuousVestingAccount) SpendableCoins() Coins {
bc := cva.GetCoins()
return min((bc + cva.DelegatedVesting) - cva.GetVestingCoins(), bc)
}
```
##### Delayed/Discrete Vesting Accounts
A delayed vesting account may send any coins it has received. In addition, if it
has fully vested, it can send any of it's vested coins.
```go
func (dva DelayedVestingAccount) SpendableCoins() Coins {
bc := dva.GetCoins()
return bc - dva.GetVestingCoins()
}
```
##### Keepers/Handlers
The corresponding `x/bank` keeper should appropriately handle sending coins
based on if the account is a vesting account or not.
```go
func SendCoins(from Account, to Account amount Coins) {
if isVesting(from) {
sc := from.SpendableCoins()
} else {
sc := from.GetCoins()
}
if amount <= sc {
from.SetCoins(sc - amount)
to.SetCoins(amount)
// save accounts...
}
}
```
### Delegating
#### Continuously Vesting Accounts
For a continuous vesting account attempting to delegate `D` coins, the following
is performed:
1. Verify `BC >= D > 0`
2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting)
3. Compute `Y := D - X` (portion of `D` that is free)
4. Set `DV += X`
5. Set `DF += Y`
6. Set `BC -= D`
```go
func (cva ContinuousVestingAccount) TrackDelegation(amount Coins) {
x := min(max(cva.GetVestingCoins() - cva.DelegatedVesting, 0), amount)
y := amount - x
cva.DelegatedVesting += x
cva.DelegatedFree += y
}
```
##### Delayed/Discrete Vesting Accounts
For a delayed vesting account, it can only delegate with received coins and
coins that are fully vested so we only need to update `DF`.
```go
func (dva DelayedVestingAccount) TrackDelegation(amount Coins) {
dva.DelegatedFree += amount
}
```
##### Keepers/Handlers
```go
func DelegateCoins(from Account, amount Coins) {
// canDelegate checks different semantics for continuous and delayed vesting
// accounts
if isVesting(from) && canDelegate(from) {
sc := from.GetCoins()
if amount <= sc {
from.TrackDelegation(amount)
from.SetCoins(sc - amount)
// save account...
}
} else {
sc := from.GetCoins()
if amount <= sc {
from.SetCoins(sc - amount)
// save account...
}
}
}
```
### Undelegating
#### Continuously Vesting Accounts
For a continuous vesting account attempting to undelegate `D` coins, the
following is performed:
1. Verify `(DV + DF) >= D > 0` (this is simply a sanity check)
2. Compute `Y := min(DF, D)` (portion of `D` that should become free, prioritizing free coins)
3. Compute `X := D - Y` (portion of `D` that should remain vesting)
4. Set `DV -= X`
5. Set `DF -= Y`
6. Set `BC += D`
```go
func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) {
y := min(cva.DelegatedFree, amount)
x := amount - y
cva.DelegatedVesting -= x
cva.DelegatedFree -= y
}
```
**Note**: If a delegation is slashed, the continuous vesting account will end up
with excess an `DV` amount, even after all its coins have vested. This is because
undelegating free coins are prioritized.
##### Delayed/Discrete Vesting Accounts
For a delayed vesting account, it only needs to add back the `DF` amount since
the account is fully vested.
```go
func (dva DelayedVestingAccount) TrackUndelegation(amount Coins) {
dva.DelegatedFree -= amount
}
```
##### Keepers/Handlers
```go
func UndelegateCoins(to Account, amount Coins) {
if isVesting(to) {
if to.DelegatedFree + to.DelegatedVesting >= amount {
to.TrackUndelegation(amount)
AddCoins(to, amount)
// save account ...
}
} else {
AddCoins(to, amount)
// save account...
}
}
```
## Keepers & Handlers
The `VestingAccount` implementations reside in `x/auth`. However, any keeper in
a module (e.g. staking in `x/stake`) wishing to potentially utilize any vesting
coins, must call explicit methods on the `x/bank` keeper (e.g. `DelegateCoins`)
opposed to `SendCoins` and `SubtractCoins`.
In addition, the vesting account should also be able to spend any coins it
receives from other users. Thus, the bank module's `MsgSend` handler should
error if a vesting account is trying to send an amount that exceeds their
unlocked coin amount.
See the above specification for full implementation details.
## Initializing at Genesis
To initialize both vesting accounts and base accounts, the `GenesisAccount`
struct will include an `EndTime`. Accounts meant to be of type `BaseAccount` will
have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into
BaseAccounts and VestingAccounts as appropriate.
```go ```go
type GenesisAccount struct { type GenesisAccount struct {
Address sdk.AccAddress `json:"address"` Address sdk.AccAddress
GenesisCoins sdk.Coins `json:"coins"` GenesisCoins sdk.Coins
EndTime int64 `json:"lock"` EndTime int64
} }
initChainer: func initChainer() {
for gacc in GenesisAccounts: for genAcc in GenesisAccounts {
baseAccount := BaseAccount{ baseAccount := BaseAccount{
Address: gacc.Address, Address: genAcc.Address,
Coins: gacc.GenesisCoins, Coins: genAcc.GenesisCoins,
} }
if gacc.EndTime != 0:
vestingAccount := ContinuouslyVestingAccount{
BaseAccount: baseAccount,
OriginalVestingCoins: gacc.GenesisCoins,
StartTime: RequestInitChain.Time,
EndTime: gacc.EndTime,
}
AddAccountToState(vestingAccount)
else:
AddAccountToState(baseAccount)
if genAcc.EndTime != 0 {
vestingAccount := ContinuousVestingAccount{
BaseAccount: baseAccount,
OriginalVesting: genAcc.GenesisCoins,
StartTime: RequestInitChain.Time,
EndTime: genAcc.EndTime,
}
AddAccountToState(vestingAccount)
} else {
AddAccountToState(baseAccount)
}
}
}
``` ```
### Formulas ## Examples
`OriginalVestingCoins`: Amount of coins in account at Genesis ### Simple
`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) Given a continuous vesting account with 10 vesting coins.
`ReceivedCoins`: Coins received from other accounts (always unlocked) ```
OV = 10
DF = 0
DV = 0
BC = 10
V = 10
V' = 0
```
`LockedCoins`: Coins that are currently locked 1. Immediately receives 1 coin
```
BC = 11
```
2. Time passes, 2 coins vest
```
V = 8
V' = 2
```
3. Delegates 4 coins to validator A
```
DV = 4
BC = 7
```
4. Sends 3 coins
```
BC = 4
```
5. More time passes, 2 more coins vest
```
V = 6
V' = 4
```
6. Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate.
```
BC = 2
```
`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked) ### Slashing
`Sent`: Coins sent to other accounts (MUST be unlocked) Same initial starting conditions as the simple example.
Maximum amount of coins vesting schedule allows to be sent: 1. Time passes, 5 coins vest
```
V = 5
V' = 5
```
2. Delegate 5 coins to validator A
```
DV = 5
BC = 5
```
3. Delegate 5 coins to validator B
```
DF = 5
BC = 0
```
4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins
5. Undelegate from validator A (2.5 coins)
```
DF = 5 - 2.5 = 2.5
BC = 0 + 2.5 = 2.5
```
6. Undelegate from validator B (5 coins). The account at this point can only send 2.5 coins unless it receives more coins or until more coins vest. It can still however, delegate.
```
DV = 5 - 2.5 = 2.5
DF = 2.5 - 2.5 = 0
BC = 2.5 + 5 = 7.5
```
`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)` Notice how we have an excess amount of `DV`.
`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins` ## Glossary
Coins currently in Account: - OriginalVesting: The amount of coins (per denomination) that are initially part of a vesting account. These coins are set at genesis.
- StartTime: The BFT time at which a vesting account starts to vest.
`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent` - EndTime: The BFT time at which a vesting account is fully vested.
- DelegatedFree: The tracked amount of coins (per denomination) that are delegated from a vesting account that have been fully vested at time of delegation.
`CurrentCoins = vestingAccount.GetCoins()` - DelegatedVesting: The tracked amount of coins (per denomination) that are delegated from a vesting account that were vesting at time of delegation.
- ContinuousVestingAccount: A vesting account implementation that vests coins linearly over time.
**Maximum amount of coins spendable right now:** - DelayedVestingAccount: A vesting account implementation that only fully vests all coins at a given time.
`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )`

View File

@ -97,9 +97,11 @@ type Proposal struct {
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
Deposits []Deposit // List of deposits on the proposal Deposits []Deposit // List of deposits on the proposal
SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included
Submitter sdk.Address // Address of the submitter DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire
Submitter sdk.AccAddress // Address of the submitter
VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached
VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end.
CurrentStatus ProposalStatus // Current status of the proposal CurrentStatus ProposalStatus // Current status of the proposal
YesVotes sdk.Dec YesVotes sdk.Dec
@ -134,46 +136,26 @@ For pseudocode purposes, here are the two function we will use to read or write
**Store:** **Store:**
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the * `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest `ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if that have reached the end of their voting period are processed.
`CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is, To process a finished proposal, the application tallies the votes, compute the votes of
then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted each validator and checks if every validator in the valdiator set have voted.
and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded. If the proposal is accepted, deposits are refunded.
After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated.
And the pseudocode for the `ProposalProcessingQueue`: And the pseudocode for the `ProposalProcessingQueue`:
```go ```go
in EndBlock do in EndBlock do
checkProposal() // First call of the recursive function for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
// Recursive function. First call in BeginBlock
func checkProposal()
proposalID = ProposalProcessingQueue.Peek()
if (proposalID == nil)
return
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
votingProcedure = load(GlobalParams, 'VotingProcedure')
if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) validators = Keeper.getAllValidators()
tmpValMap := map(sdk.AccAddress)ValidatorGovInfo
// End of voting period, tally // Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
ProposalProcessingQueue.pop()
validators =
Keeper.getAllValidators()
tmpValMap := map(sdk.Address)ValidatorGovInfo
// Initiate mapping at 0. Validators that remain at 0 at the end of tally will be punished
for each validator in validators for each validator in validators
tmpValMap(validator).Minus = 0 tmpValMap(validator.OperatorAddr).Minus = 0
// Tally // Tally
voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
@ -212,5 +194,4 @@ And the pseudocode for the `ProposalProcessingQueue`:
proposal.CurrentStatus = ProposalStatusRejected proposal.CurrentStatus = ProposalStatusRejected
store(Governance, <proposalID|'proposal'>, proposal) store(Governance, <proposalID|'proposal'>, proposal)
checkProposal()
``` ```

View File

@ -46,6 +46,8 @@ upon receiving txGovSubmitProposal from sender do
sender.AtomBalance -= initialDeposit.Atoms sender.AtomBalance -= initialDeposit.Atoms
depositProcedure = load(GlobalParams, 'DepositProcedure')
proposalID = generate new proposalID proposalID = generate new proposalID
proposal = NewProposal() proposal = NewProposal()
@ -53,28 +55,16 @@ upon receiving txGovSubmitProposal from sender do
proposal.Description = txGovSubmitProposal.Description proposal.Description = txGovSubmitProposal.Description
proposal.Type = txGovSubmitProposal.Type proposal.Type = txGovSubmitProposal.Type
proposal.TotalDeposit = initialDeposit proposal.TotalDeposit = initialDeposit
proposal.SubmitBlock = CurrentBlock proposal.SubmitTime = <CurrentTime>
proposal.DepositEndTime = <CurrentTime>.Add(depositProcedure.MaxDepositPeriod)
proposal.Deposits.append({initialDeposit, sender}) proposal.Deposits.append({initialDeposit, sender})
proposal.Submitter = sender proposal.Submitter = sender
proposal.YesVotes = 0 proposal.YesVotes = 0
proposal.NoVotes = 0 proposal.NoVotes = 0
proposal.NoWithVetoVotes = 0 proposal.NoWithVetoVotes = 0
proposal.AbstainVotes = 0 proposal.AbstainVotes = 0
depositProcedure = load(GlobalParams, 'DepositProcedure')
if (initialDeposit < depositProcedure.MinDeposit)
// MinDeposit is not reached
proposal.CurrentStatus = ProposalStatusOpen proposal.CurrentStatus = ProposalStatusOpen
else
// MinDeposit is reached
proposal.CurrentStatus = ProposalStatusActive
proposal.VotingStartBlock = CurrentBlock
ProposalProcessingQueue.push(proposalID)
store(Proposals, <proposalID|'proposal'>, proposal) // Store proposal in Proposals mapping store(Proposals, <proposalID|'proposal'>, proposal) // Store proposal in Proposals mapping
return proposalID return proposalID
``` ```

View File

@ -7,7 +7,7 @@ The staking module allow for the following hooks to be registered with staking e
type StakingHooks interface { type StakingHooks interface {
OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created
OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes
OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted OnValidatorRemoved(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator is deleted
OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding

View File

@ -49,6 +49,8 @@ func main() {
client.GetCommands( client.GetCommands(
stakecmd.GetCmdQueryValidator("stake", cdc), stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryValidatorUnbondingDelegations("stake", cdc),
stakecmd.GetCmdQueryValidatorRedelegations("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc),
stakecmd.GetCmdQueryPool("stake", cdc), stakecmd.GetCmdQueryPool("stake", cdc),

View File

@ -41,7 +41,6 @@ func main() {
appInit := server.DefaultAppInit appInit := server.DefaultAppInit
rootCmd.AddCommand(InitCmd(ctx, cdc, appInit)) rootCmd.AddCommand(InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
server.AddCommands(ctx, cdc, rootCmd, appInit, server.AddCommands(ctx, cdc, rootCmd, appInit,
newApp, exportAppStateAndTMValidators) newApp, exportAppStateAndTMValidators)
@ -85,7 +84,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err return err
} }
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) appState, err := appInit.AppGenState(
cdc, tmtypes.GenesisDoc{}, []json.RawMessage{genTx})
if err != nil { if err != nil {
return err return err
} }
@ -108,13 +108,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err return err
} }
fmt.Fprintf(os.Stderr, "%s\n", string(out)) fmt.Fprintf(os.Stderr, "%s\n", string(out))
return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID,
[]tmtypes.GenesisValidator{validator}, appStateJSON)
}, },
} }
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagChainID, "",
"genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.Flags().String(client.FlagName, "", "validator's moniker")
cmd.MarkFlagRequired(client.FlagName) cmd.MarkFlagRequired(client.FlagName)
return cmd return cmd
@ -124,7 +126,8 @@ func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Applicatio
return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning")))
} }
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) (
json.RawMessage, []tmtypes.GenesisValidator, error) {
bapp := app.NewBasecoinApp(logger, db) bapp := app.NewBasecoinApp(logger, db)
return bapp.ExportAppStateAndValidators() return bapp.ExportAppStateAndValidators()
} }

View File

@ -186,8 +186,8 @@ func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage,
genState := types.GenesisState{ genState := types.GenesisState{
Accounts: accounts, Accounts: accounts,
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), POWGenesis: pow.ExportGenesis(ctx, app.powKeeper),
CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), CoolGenesis: cool.ExportGenesis(ctx, app.coolKeeper),
} }
appState, err = codec.MarshalJSONIndent(app.cdc, genState) appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil { if err != nil {

View File

@ -21,6 +21,8 @@ import (
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli" coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli"
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli" powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli"
simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli" simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
) )
// rootCmd is the entry point for this binary // rootCmd is the entry point for this binary
@ -38,6 +40,13 @@ func main() {
// get the codec // get the codec
cdc := app.MakeCodec() cdc := app.MakeCodec()
// Setup certain SDK config
config := sdk.GetConfig()
config.SetBech32PrefixForAccount("demoacc", "demopub")
config.SetBech32PrefixForValidator("demoval", "demovalpub")
config.SetBech32PrefixForConsensusNode("democons", "democonspub")
config.Seal()
// TODO: setup keybase, viper object, etc. to be passed into // TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc // with the cdc

View File

@ -23,6 +23,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
) )
const ( const (
@ -35,8 +36,9 @@ var CoolAppInit = server.AppInit{
} }
// coolGenAppParams sets up the app_state and appends the cool app state // coolGenAppParams sets up the app_state and appends the cool app state
func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState, err = server.SimpleAppGenState(cdc, appGenTxs) appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, tmtypes.GenesisDoc{}, appGenTxs)
if err != nil { if err != nil {
return return
} }
@ -89,7 +91,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err return err
} }
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) appState, err := appInit.AppGenState(cdc, tmtypes.GenesisDoc{},
[]json.RawMessage{genTx})
if err != nil { if err != nil {
return err return err
} }
@ -112,13 +115,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err return err
} }
fmt.Fprintf(os.Stderr, "%s\n", string(out)) fmt.Fprintf(os.Stderr, "%s\n", string(out))
return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID,
[]tmtypes.GenesisValidator{validator}, appStateJSON)
}, },
} }
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagChainID, "",
"genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.Flags().String(client.FlagName, "", "validator's moniker")
cmd.MarkFlagRequired(client.FlagName) cmd.MarkFlagRequired(client.FlagName)
return cmd return cmd
@ -128,13 +133,22 @@ func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application {
return app.NewDemocoinApp(logger, db) return app.NewDemocoinApp(logger, db)
} }
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (
json.RawMessage, []tmtypes.GenesisValidator, error) {
dapp := app.NewDemocoinApp(logger, db) dapp := app.NewDemocoinApp(logger, db)
return dapp.ExportAppStateAndValidators() return dapp.ExportAppStateAndValidators()
} }
func main() { func main() {
cdc := app.MakeCodec() cdc := app.MakeCodec()
// Setup certain SDK config
config := sdk.GetConfig()
config.SetBech32PrefixForAccount("demoacc", "demopub")
config.SetBech32PrefixForValidator("demoval", "demovalpub")
config.SetBech32PrefixForConsensusNode("democons", "democonspub")
config.Seal()
ctx := server.NewDefaultContext() ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{

View File

@ -82,8 +82,13 @@ func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64,
} }
} }
// IterateValidatorsBonded implements sdk.ValidatorSet // IterateBondedValidatorsByPower implements sdk.ValidatorSet
func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { func (vs *ValidatorSet) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
vs.IterateValidators(ctx, fn)
}
// IterateLastValidators implements sdk.ValidatorSet
func (vs *ValidatorSet) IterateLastValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
vs.IterateValidators(ctx, fn) vs.IterateValidators(ctx, fn)
} }

View File

@ -49,8 +49,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error {
return nil return nil
} }
// WriteGenesis - output the genesis trend // ExportGenesis - output the genesis trend
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { func ExportGenesis(ctx sdk.Context, k Keeper) Genesis {
trend := k.GetTrend(ctx) trend := k.GetTrend(ctx)
return Genesis{trend} return Genesis{trend}
} }

View File

@ -37,7 +37,7 @@ func TestCoolKeeper(t *testing.T) {
err := InitGenesis(ctx, keeper, Genesis{"icy"}) err := InitGenesis(ctx, keeper, Genesis{"icy"})
require.Nil(t, err) require.Nil(t, err)
genesis := WriteGenesis(ctx, keeper) genesis := ExportGenesis(ctx, keeper)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, genesis, Genesis{"icy"}) require.Equal(t, genesis, Genesis{"icy"})

View File

@ -71,7 +71,7 @@ func (keeper Keeper) Info(ctx sdk.Context, p Payload) (res Info) {
if bz == nil { if bz == nil {
return EmptyInfo(ctx) return EmptyInfo(ctx)
} }
keeper.cdc.MustUnmarshalBinary(bz, &res) keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
return return
} }
@ -80,7 +80,7 @@ func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) {
store := ctx.KVStore(keeper.key) store := ctx.KVStore(keeper.key)
key := GetInfoKey(p, keeper.cdc) key := GetInfoKey(p, keeper.cdc)
bz := keeper.cdc.MustMarshalBinary(info) bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(info)
store.Set(key, bz) store.Set(key, bz)
} }

View File

@ -7,13 +7,13 @@ import (
// GetInfoKey returns the key for OracleInfo // GetInfoKey returns the key for OracleInfo
func GetInfoKey(p Payload, cdc *codec.Codec) []byte { func GetInfoKey(p Payload, cdc *codec.Codec) []byte {
bz := cdc.MustMarshalBinary(p) bz := cdc.MustMarshalBinaryLengthPrefixed(p)
return append([]byte{0x00}, bz...) return append([]byte{0x00}, bz...)
} }
// GetSignPrefix returns the prefix for signs // GetSignPrefix returns the prefix for signs
func GetSignPrefix(p Payload, cdc *codec.Codec) []byte { func GetSignPrefix(p Payload, cdc *codec.Codec) []byte {
bz := cdc.MustMarshalBinary(p) bz := cdc.MustMarshalBinaryLengthPrefixed(p)
return append([]byte{0x01}, bz...) return append([]byte{0x01}, bz...)
} }

View File

@ -82,7 +82,7 @@ func getSequence(ctx sdk.Context, key sdk.StoreKey) int {
if seqbz == nil { if seqbz == nil {
seq = 0 seq = 0
} else { } else {
codec.New().MustUnmarshalBinary(seqbz, &seq) codec.New().MustUnmarshalBinaryLengthPrefixed(seqbz, &seq)
} }
return seq return seq
@ -96,7 +96,7 @@ func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error {
return sdk.NewError(sdk.CodespaceRoot, 1, "") return sdk.NewError(sdk.CodespaceRoot, 1, "")
} }
bz := codec.New().MustMarshalBinary(seq + 1) bz := codec.New().MustMarshalBinaryLengthPrefixed(seq + 1)
store.Set([]byte("seq"), bz) store.Set([]byte("seq"), bz)
return nil return nil

View File

@ -43,8 +43,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error {
return nil return nil
} }
// WriteGenesis for the PoW module // ExportGenesis for the PoW module
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { func ExportGenesis(ctx sdk.Context, k Keeper) Genesis {
difficulty, err := k.GetLastDifficulty(ctx) difficulty, err := k.GetLastDifficulty(ctx)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -41,7 +41,7 @@ func TestPowKeeperGetSet(t *testing.T) {
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)}) err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
require.Nil(t, err) require.Nil(t, err)
genesis := WriteGenesis(ctx, keeper) genesis := ExportGenesis(ctx, keeper)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, genesis, Genesis{uint64(1), uint64(0)}) require.Equal(t, genesis, Genesis{uint64(1), uint64(0)})

View File

@ -39,7 +39,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo {
return bondInfo{} return bondInfo{}
} }
var bi bondInfo var bi bondInfo
err := k.cdc.UnmarshalBinary(bz, &bi) err := k.cdc.UnmarshalBinaryLengthPrefixed(bz, &bi)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -48,7 +48,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo {
func (k Keeper) setBondInfo(ctx sdk.Context, addr sdk.AccAddress, bi bondInfo) { func (k Keeper) setBondInfo(ctx sdk.Context, addr sdk.AccAddress, bi bondInfo) {
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
bz, err := k.cdc.MarshalBinary(bi) bz, err := k.cdc.MarshalBinaryLengthPrefixed(bi)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391) seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
blocks=$1 blocks=$1
echo "Running multi-seed simulation with seeds ${seeds[@]}" echo "Running multi-seed simulation with seeds ${seeds[@]}"

View File

@ -19,7 +19,8 @@ import (
type AppInit struct { type AppInit struct {
// AppGenState creates the core parameters initialization. It takes in a // AppGenState creates the core parameters initialization. It takes in a
// pubkey meant to represent the pubkey of the validator of this machine. // pubkey meant to represent the pubkey of the validator of this machine.
AppGenState func(cdc *codec.Codec, appGenTx []json.RawMessage) (appState json.RawMessage, err error) AppGenState func(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState json.RawMessage, err error)
} }
// SimpleGenTx is a simple genesis tx // SimpleGenTx is a simple genesis tx
@ -35,7 +36,8 @@ var DefaultAppInit = AppInit{
} }
// Generate a genesis transaction // Generate a genesis transaction
func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (
appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) {
var addr sdk.AccAddress var addr sdk.AccAddress
var secret string var secret string
addr, secret, err = GenerateCoinKey() addr, secret, err = GenerateCoinKey()
@ -63,7 +65,8 @@ func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json
} }
// create the genesis app state // create the genesis app state
func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { func SimpleAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState json.RawMessage, err error) {
if len(appGenTxs) != 1 { if len(appGenTxs) != 1 {
err = errors.New("must provide a single genesis transaction") err = errors.New("must provide a single genesis transaction")

View File

@ -3,6 +3,7 @@ package mock
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/tendermint/tendermint/types"
"path/filepath" "path/filepath"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -102,7 +103,8 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
// AppGenState can be passed into InitCmd, returns a static string of a few // AppGenState can be passed into InitCmd, returns a static string of a few
// key-values that can be parsed by InitChainer // key-values that can be parsed by InitChainer
func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { func AppGenState(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (appState json.
RawMessage, err error) {
appState = json.RawMessage(`{ appState = json.RawMessage(`{
"values": [ "values": [
{ {
@ -119,7 +121,8 @@ func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage,
} }
// AppGenStateEmpty returns an empty transaction state for mocking. // AppGenStateEmpty returns an empty transaction state for mocking.
func AppGenStateEmpty(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { func AppGenStateEmpty(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (
appState json.RawMessage, err error) {
appState = json.RawMessage(``) appState = json.RawMessage(``)
return return
} }

View File

@ -1,6 +1,7 @@
package mock package mock
import ( import (
"github.com/tendermint/tendermint/types"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -20,7 +21,7 @@ func TestInitApp(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// initialize it future-way // initialize it future-way
appState, err := AppGenState(nil, nil) appState, err := AppGenState(nil, types.GenesisDoc{}, nil)
require.NoError(t, err) require.NoError(t, err)
//TODO test validators in the init chain? //TODO test validators in the init chain?

View File

@ -93,7 +93,6 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error {
return nil return nil
} }
// nolint: unparam
func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) {
cfg := ctx.Config cfg := ctx.Config
home := cfg.RootDir home := cfg.RootDir
@ -135,7 +134,12 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) {
return nil, err return nil, err
} }
// trap signal (run forever) TrapSignal(func() {
tmNode.RunForever() if tmNode.IsRunning() {
return tmNode, nil _ = tmNode.Stop()
}
})
// run forever (the node will not be returned)
select {}
} }

View File

@ -4,7 +4,9 @@ import (
"encoding/json" "encoding/json"
"net" "net"
"os" "os"
"os/signal"
"path/filepath" "path/filepath"
"syscall"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -203,6 +205,23 @@ func ExternalIP() (string, error) {
return "", errors.New("are you connected to the network?") return "", errors.New("are you connected to the network?")
} }
// TrapSignal traps SIGINT and SIGTERM and terminates the server correctly.
func TrapSignal(cleanupFunc func()) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
switch sig {
case syscall.SIGTERM:
defer cleanupFunc()
os.Exit(128 + int(syscall.SIGTERM))
case syscall.SIGINT:
defer cleanupFunc()
os.Exit(128 + int(syscall.SIGINT))
}
}()
}
func skipInterface(iface net.Interface) bool { func skipInterface(iface net.Interface) bool {
if iface.Flags&net.FlagUp == 0 { if iface.Flags&net.FlagUp == 0 {
return true // interface down return true // interface down

View File

@ -61,6 +61,7 @@ func (ci *cacheKVStore) Set(key []byte, value []byte) {
ci.mtx.Lock() ci.mtx.Lock()
defer ci.mtx.Unlock() defer ci.mtx.Unlock()
ci.assertValidKey(key) ci.assertValidKey(key)
ci.assertValidValue(value)
ci.setCacheValue(key, value, false, true) ci.setCacheValue(key, value, false, true)
} }
@ -196,6 +197,12 @@ func (ci *cacheKVStore) assertValidKey(key []byte) {
} }
} }
func (ci *cacheKVStore) assertValidValue(value []byte) {
if value == nil {
panic("value is nil")
}
}
// Only entrypoint to mutate ci.cache. // Only entrypoint to mutate ci.cache.
func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) { func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) {
ci.cache[string(key)] = cValue{ ci.cache[string(key)] = cValue{

View File

@ -5,9 +5,9 @@ import (
"io" "io"
"sync" "sync"
"github.com/tendermint/go-amino"
"github.com/tendermint/iavl" "github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
@ -210,44 +210,59 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
res.Height = getHeight(tree, req) res.Height = getHeight(tree, req)
switch req.Path { switch req.Path {
case "/store", "/key": // Get by key case "/key": // get by key
key := req.Data // Data holds the key bytes key := req.Data // data holds the key bytes
res.Key = key res.Key = key
if !st.VersionExists(res.Height) { if !st.VersionExists(res.Height) {
res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error() res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error()
break break
} }
if req.Prove { if req.Prove {
value, proof, err := tree.GetVersionedWithProof(key, res.Height) value, proof, err := tree.GetVersionedWithProof(key, res.Height)
if err != nil { if err != nil {
res.Log = err.Error() res.Log = err.Error()
break break
} }
res.Value = value if proof == nil {
cdc := amino.NewCodec() // Proof == nil implies that the store is empty.
p, err := cdc.MarshalBinary(proof) if value != nil {
if err != nil { panic("unexpected value for an empty proof")
res.Log = err.Error() }
break }
if value != nil {
// value was found
res.Value = value
res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}}
} else {
// value wasn't found
res.Value = nil
res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLAbsenceOp(key, proof).ProofOp()}}
} }
res.Proof = p
} else { } else {
_, res.Value = tree.GetVersioned(key, res.Height) _, res.Value = tree.GetVersioned(key, res.Height)
} }
case "/subspace": case "/subspace":
var KVs []KVPair
subspace := req.Data subspace := req.Data
res.Key = subspace res.Key = subspace
var KVs []KVPair
iterator := sdk.KVStorePrefixIterator(st, subspace) iterator := sdk.KVStorePrefixIterator(st, subspace)
for ; iterator.Valid(); iterator.Next() { for ; iterator.Valid(); iterator.Next() {
KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()})
} }
iterator.Close() iterator.Close()
res.Value = cdc.MustMarshalBinary(KVs) res.Value = cdc.MustMarshalBinaryLengthPrefixed(KVs)
default: default:
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
return sdk.ErrUnknownRequest(msg).QueryResult() return sdk.ErrUnknownRequest(msg).QueryResult()
} }
return return
} }

View File

@ -394,9 +394,9 @@ func TestIAVLStoreQuery(t *testing.T) {
{Key: k1, Value: v3}, {Key: k1, Value: v3},
{Key: k2, Value: v2}, {Key: k2, Value: v2},
} }
valExpSubEmpty := cdc.MustMarshalBinary(KVs0) valExpSubEmpty := cdc.MustMarshalBinaryLengthPrefixed(KVs0)
valExpSub1 := cdc.MustMarshalBinary(KVs1) valExpSub1 := cdc.MustMarshalBinaryLengthPrefixed(KVs1)
valExpSub2 := cdc.MustMarshalBinary(KVs2) valExpSub2 := cdc.MustMarshalBinaryLengthPrefixed(KVs2)
cid := iavlStore.Commit() cid := iavlStore.Commit()
ver := cid.Version ver := cid.Version
@ -459,7 +459,7 @@ func TestIAVLStoreQuery(t *testing.T) {
require.Equal(t, valExpSub2, qres.Value) require.Equal(t, valExpSub2, qres.Value)
// default (height 0) will show latest -1 // default (height 0) will show latest -1
query0 := abci.RequestQuery{Path: "/store", Data: k1} query0 := abci.RequestQuery{Path: "/key", Data: k1}
qres = iavlStore.Query(query0) qres = iavlStore.Query(query0)
require.Equal(t, uint32(sdk.CodeOK), qres.Code) require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v1, qres.Value) require.Equal(t, v1, qres.Value)

View File

@ -42,21 +42,22 @@ func (m List) Len() (res uint64) {
if bz == nil { if bz == nil {
return 0 return 0
} }
m.cdc.MustUnmarshalBinary(bz, &res)
m.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
return return
} }
// Get() returns the element by its index // Get() returns the element by its index
func (m List) Get(index uint64, ptr interface{}) error { func (m List) Get(index uint64, ptr interface{}) error {
bz := m.store.Get(ElemKey(index)) bz := m.store.Get(ElemKey(index))
return m.cdc.UnmarshalBinary(bz, ptr) return m.cdc.UnmarshalBinaryLengthPrefixed(bz, ptr)
} }
// Set() stores the element to the given position // Set() stores the element to the given position
// Setting element out of range will break length counting // Setting element out of range will break length counting
// Use Push() instead of Set() to append a new element // Use Push() instead of Set() to append a new element
func (m List) Set(index uint64, value interface{}) { func (m List) Set(index uint64, value interface{}) {
bz := m.cdc.MustMarshalBinary(value) bz := m.cdc.MustMarshalBinaryLengthPrefixed(value)
m.store.Set(ElemKey(index), bz) m.store.Set(ElemKey(index), bz)
} }
@ -72,7 +73,7 @@ func (m List) Delete(index uint64) {
func (m List) Push(value interface{}) { func (m List) Push(value interface{}) {
length := m.Len() length := m.Len()
m.Set(length, value) m.Set(length, value)
m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1)) m.store.Set(LengthKey(), m.cdc.MustMarshalBinaryLengthPrefixed(length+1))
} }
// Iterate() is used to iterate over all existing elements in the list // Iterate() is used to iterate over all existing elements in the list
@ -85,13 +86,16 @@ func (m List) Iterate(ptr interface{}, fn func(uint64) bool) {
iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01})
for ; iter.Valid(); iter.Next() { for ; iter.Valid(); iter.Next() {
v := iter.Value() v := iter.Value()
m.cdc.MustUnmarshalBinary(v, ptr) m.cdc.MustUnmarshalBinaryLengthPrefixed(v, ptr)
k := iter.Key() k := iter.Key()
s := string(k[len(k)-20:]) s := string(k[len(k)-20:])
index, err := strconv.ParseUint(s, 10, 64) index, err := strconv.ParseUint(s, 10, 64)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if fn(index) { if fn(index) {
break break
} }

View File

@ -2,90 +2,139 @@ package store
import ( import (
"bytes" "bytes"
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/iavl" "github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
) )
// MultiStoreProof defines a collection of store proofs in a multi-store // MultiStoreProof defines a collection of store proofs in a multi-store
type MultiStoreProof struct { type MultiStoreProof struct {
StoreInfos []storeInfo StoreInfos []storeInfo
StoreName string
RangeProof iavl.RangeProof
} }
// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof {
func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte { return &MultiStoreProof{StoreInfos: storeInfos}
var rangeProof iavl.RangeProof
cdc.MustUnmarshalBinary(iavlProof, &rangeProof)
msp := MultiStoreProof{
StoreInfos: storeInfos,
StoreName: storeName,
RangeProof: rangeProof,
}
proof := cdc.MustMarshalBinary(msp)
return proof
} }
// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash // ComputeRootHash returns the root hash for a given multi-store proof.
func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) { func (proof *MultiStoreProof) ComputeRootHash() []byte {
var substoreCommitHash []byte
var height int64
for _, storeInfo := range storeInfos {
if storeInfo.Name == storeName {
substoreCommitHash = storeInfo.Core.CommitID.Hash
height = storeInfo.Core.CommitID.Version
}
}
if len(substoreCommitHash) == 0 {
return nil, cmn.NewError("failed to get substore root commit hash by store name")
}
ci := commitInfo{ ci := commitInfo{
Version: height, Version: -1, // TODO: Not needed; improve code.
StoreInfos: storeInfos, StoreInfos: proof.StoreInfos,
} }
if !bytes.Equal(appHash, ci.Hash()) { return ci.Hash()
return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash")
}
return substoreCommitHash, nil
} }
// VerifyRangeProof verify iavl RangeProof // RequireProof returns whether proof is required for the subpath.
func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error {
// verify the proof to ensure data integrity.
err := rangeProof.Verify(substoreCommitHash)
if err != nil {
return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash")
}
if len(value) != 0 {
// verify existence proof
err = rangeProof.VerifyItem(key, value)
if err != nil {
return errors.Wrap(err, "failed in existence verification")
}
} else {
// verify absence proof
err = rangeProof.VerifyAbsence(key)
if err != nil {
return errors.Wrap(err, "failed in absence verification")
}
}
return nil
}
// RequireProof return whether proof is require for the subpath
func RequireProof(subpath string) bool { func RequireProof(subpath string) bool {
// Currently, only when query subpath is "/store" or "/key", will proof be included in response. // XXX: create a better convention.
// If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212 // Currently, only when query subpath is "/key", will proof be included in
if subpath == "/store" || subpath == "/key" { // response. If there are some changes about proof building in iavlstore.go,
// we must change code here to keep consistency with iavlStore#Query.
if subpath == "/key" {
return true return true
} }
return false return false
} }
//-----------------------------------------------------------------------------
var _ merkle.ProofOperator = MultiStoreProofOp{}
// the multi-store proof operation constant value
const ProofOpMultiStore = "multistore"
// TODO: document
type MultiStoreProofOp struct {
// Encoded in ProofOp.Key
key []byte
// To encode in ProofOp.Data.
Proof *MultiStoreProof `json:"proof"`
}
func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp {
return MultiStoreProofOp{
key: key,
Proof: proof,
}
}
// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a
// given proof operation.
func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) {
if pop.Type != ProofOpMultiStore {
return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore)
}
// XXX: a bit strange as we'll discard this, but it works
var op MultiStoreProofOp
err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into MultiStoreProofOp")
}
return NewMultiStoreProofOp(pop.Key, op.Proof), nil
}
// ProofOp return a merkle proof operation from a given multi-store proof
// operation.
func (op MultiStoreProofOp) ProofOp() merkle.ProofOp {
bz := cdc.MustMarshalBinaryLengthPrefixed(op)
return merkle.ProofOp{
Type: ProofOpMultiStore,
Key: op.key,
Data: bz,
}
}
// String implements the Stringer interface for a mult-store proof operation.
func (op MultiStoreProofOp) String() string {
return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey())
}
// GetKey returns the key for a multi-store proof operation.
func (op MultiStoreProofOp) GetKey() []byte {
return op.key
}
// Run executes a multi-store proof operation for a given value. It returns
// the root hash if the value matches all the store's commitID's hash or an
// error otherwise.
func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) {
if len(args) != 1 {
return nil, cmn.NewError("Value size is not 1")
}
value := args[0]
root := op.Proof.ComputeRootHash()
for _, si := range op.Proof.StoreInfos {
if si.Name == string(op.key) {
if bytes.Equal(value, si.Core.CommitID.Hash) {
return [][]byte{root}, nil
}
return nil, cmn.NewError("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value)
}
}
return nil, cmn.NewError("key %v not found in multistore proof", op.key)
}
//-----------------------------------------------------------------------------
// XXX: This should be managed by the rootMultiStore which may want to register
// more proof ops?
func DefaultProofRuntime() (prt *merkle.ProofRuntime) {
prt = merkle.NewProofRuntime()
prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder)
prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder)
prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder)
prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder)
return
}

View File

@ -1,123 +1,174 @@
package store package store
import ( import (
"encoding/hex"
"testing" "testing"
"github.com/stretchr/testify/assert" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/db"
) )
func TestVerifyMultiStoreCommitInfo(t *testing.T) { func TestVerifyIAVLStoreQueryProof(t *testing.T) {
appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") // Create main tree for testing.
db := dbm.NewMemDB()
substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") iStore, err := LoadIAVLStore(db, CommitID{}, sdk.PruneNothing)
storeName := "acc" store := iStore.(*iavlStore)
var storeInfos []storeInfo
gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78")
storeInfos = append(storeInfos, storeInfo{
Name: "gov",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: gocRootHash,
},
},
})
storeInfos = append(storeInfos, storeInfo{
Name: "main",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: nil,
},
},
})
accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
storeInfos = append(storeInfos, storeInfo{
Name: "acc",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: accRootHash,
},
},
})
storeInfos = append(storeInfos, storeInfo{
Name: "ibc",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: nil,
},
},
})
stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4")
storeInfos = append(storeInfos, storeInfo{
Name: "stake",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: stakeRootHash,
},
},
})
slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e")
storeInfos = append(storeInfos, storeInfo{
Name: "slashing",
Core: storeCore{
CommitID: CommitID{
Version: 689,
Hash: slashingRootHash,
},
},
})
commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, commitHash, substoreRootHash) store.Set([]byte("MYKEY"), []byte("MYVALUE"))
cid := store.Commit()
appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3") // Get Proof
res := store.Query(abci.RequestQuery{
Path: "/key", // required path to get key/value+proof
Data: []byte("MYKEY"),
Prove: true,
})
require.NotNil(t, res.Proof)
_, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) // Verify proof.
require.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo") prt := DefaultProofRuntime()
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE"))
require.Nil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY_NOT", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY/MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE_NOT"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte(nil))
require.NotNil(t, err)
} }
func TestVerifyRangeProof(t *testing.T) { func TestVerifyMultiStoreQueryProof(t *testing.T) {
tree := iavl.NewMutableTree(db.NewMemDB(), 0) // Create main tree for testing.
db := dbm.NewMemDB()
store := NewCommitMultiStore(db)
iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey")
rand := cmn.NewRand() store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil)
rand.Seed(0) // for determinism store.LoadVersion(0)
for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} {
key := []byte{ikey}
tree.Set(key, []byte(rand.Str(8)))
}
root := tree.WorkingHash() iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore)
iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE"))
cid := store.Commit()
key := []byte{0x32} // Get Proof
val, proof, err := tree.GetWithProof(key) res := store.Query(abci.RequestQuery{
assert.Nil(t, err) Path: "/iavlStoreKey/key", // required path to get key/value+proof
assert.NotEmpty(t, val) Data: []byte("MYKEY"),
assert.NotEmpty(t, proof) Prove: true,
err = VerifyRangeProof(key, val, root, proof) })
assert.Nil(t, err) require.NotNil(t, res.Proof)
key = []byte{0x40} // Verify proof.
val, proof, err = tree.GetWithProof(key) prt := DefaultProofRuntime()
assert.Nil(t, err) err := prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE"))
assert.Empty(t, val) require.Nil(t, err)
assert.NotEmpty(t, proof)
err = VerifyRangeProof(key, val, root, proof) // Verify proof.
assert.Nil(t, err) err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE"))
require.Nil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY_NOT", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY/MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "iavlStoreKey/MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE_NOT"))
require.NotNil(t, err)
// Verify (bad) proof.
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil))
require.NotNil(t, err)
}
func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) {
// Create main tree for testing.
db := dbm.NewMemDB()
store := NewCommitMultiStore(db)
iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey")
store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil)
store.LoadVersion(0)
cid := store.Commit() // Commit with empty iavl store.
// Get Proof
res := store.Query(abci.RequestQuery{
Path: "/iavlStoreKey/key", // required path to get key/value+proof
Data: []byte("MYKEY"),
Prove: true,
})
require.NotNil(t, res.Proof)
// Verify proof.
prt := DefaultProofRuntime()
err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY")
require.Nil(t, err)
// Verify (bad) proof.
prt = DefaultProofRuntime()
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE"))
require.NotNil(t, err)
}
func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) {
// Create main tree for testing.
db := dbm.NewMemDB()
store := NewCommitMultiStore(db)
iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey")
store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil)
store.LoadVersion(0)
iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore)
iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE"))
cid := store.Commit() // Commit with empty iavl store.
// Get Proof
res := store.Query(abci.RequestQuery{
Path: "/iavlStoreKey/key", // required path to get key/value+proof
Data: []byte("MYABSENTKEY"),
Prove: true,
})
require.NotNil(t, res.Proof)
// Verify proof.
prt := DefaultProofRuntime()
err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY")
require.Nil(t, err)
// Verify (bad) proof.
prt = DefaultProofRuntime()
err = prt.VerifyAbsence(res.Proof, cid.Hash, "/MYABSENTKEY")
require.NotNil(t, err)
// Verify (bad) proof.
prt = DefaultProofRuntime()
err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY", []byte(""))
require.NotNil(t, err)
} }

View File

@ -27,12 +27,12 @@ func (m Queue) getTop() (res uint64) {
return 0 return 0
} }
m.List.cdc.MustUnmarshalBinary(bz, &res) m.List.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
return return
} }
func (m Queue) setTop(top uint64) { func (m Queue) setTop(top uint64) {
bz := m.List.cdc.MustMarshalBinary(top) bz := m.List.cdc.MustMarshalBinaryLengthPrefixed(top)
m.List.store.Set(TopKey(), bz) m.List.store.Set(TopKey(), bz)
} }

View File

@ -81,7 +81,7 @@ func TestKeys(t *testing.T) {
var actual int var actual int
// Checking keys.LengthKey // Checking keys.LengthKey
err := cdc.UnmarshalBinary(store.Get(LengthKey()), &len) err := cdc.UnmarshalBinaryLengthPrefixed(store.Get(LengthKey()), &len)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, len, queue.List.Len()) require.Equal(t, len, queue.List.Len())
@ -89,14 +89,14 @@ func TestKeys(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
queue.List.Get(uint64(i), &expected) queue.List.Get(uint64(i), &expected)
bz := store.Get(ElemKey(uint64(i))) bz := store.Get(ElemKey(uint64(i)))
err = cdc.UnmarshalBinary(bz, &actual) err = cdc.UnmarshalBinaryLengthPrefixed(bz, &actual)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
} }
queue.Pop() queue.Pop()
err = cdc.UnmarshalBinary(store.Get(TopKey()), &top) err = cdc.UnmarshalBinaryLengthPrefixed(store.Get(TopKey()), &top)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, top, queue.getTop()) require.Equal(t, top, queue.getTop())
} }

View File

@ -295,13 +295,23 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
return res return res
} }
if res.Proof == nil || len(res.Proof.Ops) == 0 {
return sdk.ErrInternal("substore proof was nil/empty when it should never be").QueryResult()
}
commitInfo, errMsg := getCommitInfo(rs.db, res.Height) commitInfo, errMsg := getCommitInfo(rs.db, res.Height)
if errMsg != nil { if errMsg != nil {
return sdk.ErrInternal(errMsg.Error()).QueryResult() return sdk.ErrInternal(errMsg.Error()).QueryResult()
} }
res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) // Restore origin path and append proof op.
res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp(
[]byte(storeName),
NewMultiStoreProof(commitInfo.StoreInfos),
).ProofOp())
// TODO: handle in another TM v0.26 update PR
// res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos)
return res return res
} }
@ -313,11 +323,14 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) {
err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path))
return return
} }
paths := strings.SplitN(path[1:], "/", 2) paths := strings.SplitN(path[1:], "/", 2)
storeName = paths[0] storeName = paths[0]
if len(paths) == 2 { if len(paths) == 2 {
subpath = "/" + paths[1] subpath = "/" + paths[1]
} }
return return
} }
@ -386,11 +399,12 @@ type commitInfo struct {
// Hash returns the simple merkle root hash of the stores sorted by name. // Hash returns the simple merkle root hash of the stores sorted by name.
func (ci commitInfo) Hash() []byte { func (ci commitInfo) Hash() []byte {
// TODO cache to ci.hash []byte // TODO: cache to ci.hash []byte
m := make(map[string]merkle.Hasher, len(ci.StoreInfos)) m := make(map[string][]byte, len(ci.StoreInfos))
for _, storeInfo := range ci.StoreInfos { for _, storeInfo := range ci.StoreInfos {
m[storeInfo.Name] = storeInfo m[storeInfo.Name] = storeInfo.Hash()
} }
return merkle.SimpleHashFromMap(m) return merkle.SimpleHashFromMap(m)
} }
@ -422,13 +436,15 @@ type storeCore struct {
func (si storeInfo) Hash() []byte { func (si storeInfo) Hash() []byte {
// Doesn't write Name, since merkle.SimpleHashFromMap() will // Doesn't write Name, since merkle.SimpleHashFromMap() will
// include them via the keys. // include them via the keys.
bz, _ := cdc.MarshalBinary(si.Core) // Does not error bz, _ := cdc.MarshalBinaryLengthPrefixed(si.Core)
hasher := tmhash.New() hasher := tmhash.New()
_, err := hasher.Write(bz) _, err := hasher.Write(bz)
if err != nil { if err != nil {
// TODO: Handle with #870 // TODO: Handle with #870
panic(err) panic(err)
} }
return hasher.Sum(nil) return hasher.Sum(nil)
} }
@ -441,16 +457,18 @@ func getLatestVersion(db dbm.DB) int64 {
if latestBytes == nil { if latestBytes == nil {
return 0 return 0
} }
err := cdc.UnmarshalBinary(latestBytes, &latest)
err := cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return latest return latest
} }
// Set the latest version. // Set the latest version.
func setLatestVersion(batch dbm.Batch, version int64) { func setLatestVersion(batch dbm.Batch, version int64) {
latestBytes, _ := cdc.MarshalBinary(version) // Does not error latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version)
batch.Set([]byte(latestVersionKey), latestBytes) batch.Set([]byte(latestVersionKey), latestBytes)
} }
@ -491,21 +509,19 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) {
return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data") return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data")
} }
// Parse bytes.
var cInfo commitInfo var cInfo commitInfo
err := cdc.UnmarshalBinary(cInfoBytes, &cInfo)
err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo)
if err != nil { if err != nil {
return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err) return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err)
} }
return cInfo, nil return cInfo, nil
} }
// Set a commitInfo for given version. // Set a commitInfo for given version.
func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) {
cInfoBytes, err := cdc.MarshalBinary(cInfo) cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo)
if err != nil {
panic(err)
}
cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version)
batch.Set([]byte(cInfoKey), cInfoBytes) batch.Set([]byte(cInfoKey), cInfoBytes)
} }

View File

@ -215,7 +215,7 @@ func getExpectedCommitID(store *rootMultiStore, ver int64) CommitID {
} }
func hashStores(stores map[StoreKey]CommitStore) []byte { func hashStores(stores map[StoreKey]CommitStore) []byte {
m := make(map[string]merkle.Hasher, len(stores)) m := make(map[string][]byte, len(stores))
for key, store := range stores { for key, store := range stores {
name := key.Name() name := key.Name()
m[name] = storeInfo{ m[name] = storeInfo{
@ -224,7 +224,7 @@ func hashStores(stores map[StoreKey]CommitStore) []byte {
CommitID: store.LastCommitID(), CommitID: store.LastCommitID(),
// StoreType: store.GetStoreType(), // StoreType: store.GetStoreType(),
}, },
} }.Hash()
} }
return merkle.SimpleHashFromMap(m) return merkle.SimpleHashFromMap(m)
} }

View File

@ -55,7 +55,8 @@ func AccAddressFromHex(address string) (addr AccAddress, err error) {
// AccAddressFromBech32 creates an AccAddress from a Bech32 string. // AccAddressFromBech32 creates an AccAddress from a Bech32 string.
func AccAddressFromBech32(address string) (addr AccAddress, err error) { func AccAddressFromBech32(address string) (addr AccAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixAccAddr) bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixAccAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -124,7 +125,8 @@ func (aa AccAddress) Bytes() []byte {
// String implements the Stringer interface. // String implements the Stringer interface.
func (aa AccAddress) String() string { func (aa AccAddress) String() string {
bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixAccAddr, aa.Bytes()) bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix()
bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixAccAddr, aa.Bytes())
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -169,7 +171,8 @@ func ValAddressFromHex(address string) (addr ValAddress, err error) {
// ValAddressFromBech32 creates a ValAddress from a Bech32 string. // ValAddressFromBech32 creates a ValAddress from a Bech32 string.
func ValAddressFromBech32(address string) (addr ValAddress, err error) { func ValAddressFromBech32(address string) (addr ValAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixValAddr) bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixValAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -239,7 +242,8 @@ func (va ValAddress) Bytes() []byte {
// String implements the Stringer interface. // String implements the Stringer interface.
func (va ValAddress) String() string { func (va ValAddress) String() string {
bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixValAddr, va.Bytes()) bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix()
bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixValAddr, va.Bytes())
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -284,7 +288,8 @@ func ConsAddressFromHex(address string) (addr ConsAddress, err error) {
// ConsAddressFromBech32 creates a ConsAddress from a Bech32 string. // ConsAddressFromBech32 creates a ConsAddress from a Bech32 string.
func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { func ConsAddressFromBech32(address string) (addr ConsAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixConsAddr) bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixConsAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -359,7 +364,8 @@ func (ca ConsAddress) Bytes() []byte {
// String implements the Stringer interface. // String implements the Stringer interface.
func (ca ConsAddress) String() string { func (ca ConsAddress) String() string {
bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixConsAddr, ca.Bytes()) bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix()
bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixConsAddr, ca.Bytes())
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -387,7 +393,8 @@ func (ca ConsAddress) Format(s fmt.State, verb rune) {
// Bech32ifyAccPub returns a Bech32 encoded string containing the // Bech32ifyAccPub returns a Bech32 encoded string containing the
// Bech32PrefixAccPub prefix for a given account PubKey. // Bech32PrefixAccPub prefix for a given account PubKey.
func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { func Bech32ifyAccPub(pub crypto.PubKey) (string, error) {
return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix()
return bech32.ConvertAndEncode(bech32PrefixAccPub, pub.Bytes())
} }
// MustBech32ifyAccPub returns the result of Bech32ifyAccPub panicing on failure. // MustBech32ifyAccPub returns the result of Bech32ifyAccPub panicing on failure.
@ -403,7 +410,8 @@ func MustBech32ifyAccPub(pub crypto.PubKey) string {
// Bech32ifyValPub returns a Bech32 encoded string containing the // Bech32ifyValPub returns a Bech32 encoded string containing the
// Bech32PrefixValPub prefix for a given validator operator's PubKey. // Bech32PrefixValPub prefix for a given validator operator's PubKey.
func Bech32ifyValPub(pub crypto.PubKey) (string, error) { func Bech32ifyValPub(pub crypto.PubKey) (string, error) {
return bech32.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes()) bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix()
return bech32.ConvertAndEncode(bech32PrefixValPub, pub.Bytes())
} }
// MustBech32ifyValPub returns the result of Bech32ifyValPub panicing on failure. // MustBech32ifyValPub returns the result of Bech32ifyValPub panicing on failure.
@ -419,7 +427,8 @@ func MustBech32ifyValPub(pub crypto.PubKey) string {
// Bech32ifyConsPub returns a Bech32 encoded string containing the // Bech32ifyConsPub returns a Bech32 encoded string containing the
// Bech32PrefixConsPub prefixfor a given consensus node's PubKey. // Bech32PrefixConsPub prefixfor a given consensus node's PubKey.
func Bech32ifyConsPub(pub crypto.PubKey) (string, error) { func Bech32ifyConsPub(pub crypto.PubKey) (string, error) {
return bech32.ConvertAndEncode(Bech32PrefixConsPub, pub.Bytes()) bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix()
return bech32.ConvertAndEncode(bech32PrefixConsPub, pub.Bytes())
} }
// MustBech32ifyConsPub returns the result of Bech32ifyConsPub panicing on // MustBech32ifyConsPub returns the result of Bech32ifyConsPub panicing on
@ -436,7 +445,8 @@ func MustBech32ifyConsPub(pub crypto.PubKey) string {
// GetAccPubKeyBech32 creates a PubKey for an account with a given public key // GetAccPubKeyBech32 creates a PubKey for an account with a given public key
// string using the Bech32 Bech32PrefixAccPub prefix. // string using the Bech32 Bech32PrefixAccPub prefix.
func GetAccPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { func GetAccPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
bz, err := GetFromBech32(pubkey, Bech32PrefixAccPub) bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix()
bz, err := GetFromBech32(pubkey, bech32PrefixAccPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -463,7 +473,8 @@ func MustGetAccPubKeyBech32(pubkey string) (pk crypto.PubKey) {
// GetValPubKeyBech32 creates a PubKey for a validator's operator with a given // GetValPubKeyBech32 creates a PubKey for a validator's operator with a given
// public key string using the Bech32 Bech32PrefixValPub prefix. // public key string using the Bech32 Bech32PrefixValPub prefix.
func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
bz, err := GetFromBech32(pubkey, Bech32PrefixValPub) bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix()
bz, err := GetFromBech32(pubkey, bech32PrefixValPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -490,7 +501,8 @@ func MustGetValPubKeyBech32(pubkey string) (pk crypto.PubKey) {
// GetConsPubKeyBech32 creates a PubKey for a consensus node with a given public // GetConsPubKeyBech32 creates a PubKey for a consensus node with a given public
// key string using the Bech32 Bech32PrefixConsPub prefix. // key string using the Bech32 Bech32PrefixConsPub prefix.
func GetConsPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { func GetConsPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
bz, err := GetFromBech32(pubkey, Bech32PrefixConsPub) bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix()
bz, err := GetFromBech32(pubkey, bech32PrefixConsPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,6 +9,8 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
"strings"
"github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types"
) )
@ -178,3 +180,44 @@ func TestConsAddress(t *testing.T) {
require.NotNil(t, err) require.NotNil(t, err)
} }
} }
const letterBytes = "abcdefghijklmnopqrstuvwxyz"
func RandString(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func TestConfiguredPrefix(t *testing.T) {
var pub ed25519.PubKeyEd25519
for length := 1; length < 10; length++ {
for times := 1; times < 20; times++ {
rand.Read(pub[:])
// Test if randomly generated prefix of a given length works
prefix := RandString(length)
// Assuming that GetConfig is not sealed.
config := types.GetConfig()
config.SetBech32PrefixForAccount(prefix+"acc", prefix+"pub")
acc := types.AccAddress(pub.Address())
require.True(t, strings.HasPrefix(acc.String(), prefix+"acc"))
bech32Pub := types.MustBech32ifyAccPub(pub)
require.True(t, strings.HasPrefix(bech32Pub, prefix+"pub"))
config.SetBech32PrefixForValidator(prefix+"valaddr", prefix+"valpub")
val := types.ValAddress(pub.Address())
require.True(t, strings.HasPrefix(val.String(), prefix+"valaddr"))
bech32ValPub := types.MustBech32ifyValPub(pub)
require.True(t, strings.HasPrefix(bech32ValPub, prefix+"valpub"))
config.SetBech32PrefixForConsensusNode(prefix+"consaddr", prefix+"conspub")
cons := types.ConsAddress(pub.Address())
require.True(t, strings.HasPrefix(cons.String(), prefix+"consaddr"))
bech32ConsPub := types.MustBech32ifyConsPub(pub)
require.True(t, strings.HasPrefix(bech32ConsPub, prefix+"conspub"))
}
}
}

View File

@ -49,7 +49,7 @@ func (coin Coin) IsGTE(other Coin) bool {
// IsLT returns true if they are the same type and the receiver is // IsLT returns true if they are the same type and the receiver is
// a smaller value // a smaller value
func (coin Coin) IsLT(other Coin) bool { func (coin Coin) IsLT(other Coin) bool {
return !coin.IsGTE(other) return coin.SameDenomAs(other) && coin.Amount.LT(other.Amount)
} }
// IsEqual returns true if the two sets of Coins have the same value // IsEqual returns true if the two sets of Coins have the same value
@ -142,7 +142,11 @@ func (coins Coins) Plus(coinsB Coins) Coins {
coinA, coinB := coins[indexA], coinsB[indexB] coinA, coinB := coins[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) { switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1: case -1:
if coinA.IsZero() {
// ignore 0 sum coin type
} else {
sum = append(sum, coinA) sum = append(sum, coinA)
}
indexA++ indexA++
case 0: case 0:
if coinA.Amount.Add(coinB.Amount).IsZero() { if coinA.Amount.Add(coinB.Amount).IsZero() {
@ -153,7 +157,11 @@ func (coins Coins) Plus(coinsB Coins) Coins {
indexA++ indexA++
indexB++ indexB++
case 1: case 1:
if coinB.IsZero() {
// ignore 0 sum coin type
} else {
sum = append(sum, coinB) sum = append(sum, coinB)
}
indexB++ indexB++
} }
} }
@ -176,10 +184,19 @@ func (coins Coins) Minus(coinsB Coins) Coins {
return coins.Plus(coinsB.Negative()) return coins.Plus(coinsB.Negative())
} }
// IsGTE returns True iff coins is NonNegative(), and for every // IsAllGT returns True iff for every denom in coins, the denom is present at a
// currency in coinsB, the currency is present at an equal or greater // greater amount in coinsB.
// amount in coinsB func (coins Coins) IsAllGT(coinsB Coins) bool {
func (coins Coins) IsGTE(coinsB Coins) bool { diff := coins.Minus(coinsB)
if len(diff) == 0 {
return false
}
return diff.IsPositive()
}
// IsAllGTE returns True iff for every denom in coins, the denom is present at an
// equal or greater amount in coinsB.
func (coins Coins) IsAllGTE(coinsB Coins) bool {
diff := coins.Minus(coinsB) diff := coins.Minus(coinsB)
if len(diff) == 0 { if len(diff) == 0 {
return true return true
@ -187,14 +204,27 @@ func (coins Coins) IsGTE(coinsB Coins) bool {
return diff.IsNotNegative() return diff.IsNotNegative()
} }
// IsLT returns True iff every currency in coins, the currency is // IsAllLT returns True iff for every denom in coins, the denom is present at
// present at a smaller amount in coins // a smaller amount in coinsB.
func (coins Coins) IsLT(coinsB Coins) bool { func (coins Coins) IsAllLT(coinsB Coins) bool {
return !coins.IsGTE(coinsB) diff := coinsB.Minus(coins)
if len(diff) == 0 {
return false
}
return diff.IsPositive()
} }
// IsZero returns true if there are no coins // IsAllLTE returns True iff for every denom in coins, the denom is present at
// or all coins are zero. // a smaller or equal amount in coinsB.
func (coins Coins) IsAllLTE(coinsB Coins) bool {
diff := coinsB.Minus(coins)
if len(diff) == 0 {
return true
}
return diff.IsNotNegative()
}
// IsZero returns true if there are no coins or all coins are zero.
func (coins Coins) IsZero() bool { func (coins Coins) IsZero() bool {
for _, coin := range coins { for _, coin := range coins {
if !coin.IsZero() { if !coin.IsZero() {

View File

@ -86,7 +86,10 @@ func TestIsLTCoin(t *testing.T) {
{NewInt64Coin("A", 1), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", 1), NewInt64Coin("A", 1), false},
{NewInt64Coin("A", 2), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", 2), NewInt64Coin("A", 1), false},
{NewInt64Coin("A", -1), NewInt64Coin("A", 5), true}, {NewInt64Coin("A", -1), NewInt64Coin("A", 5), true},
{NewInt64Coin("a", 0), NewInt64Coin("b", 1), true}, {NewInt64Coin("a", 0), NewInt64Coin("b", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("b", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("a", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("a", 2), true},
} }
for tcIndex, tc := range cases { for tcIndex, tc := range cases {
@ -245,9 +248,9 @@ func TestCoins(t *testing.T) {
assert.True(t, good.IsValid(), "Coins are valid") assert.True(t, good.IsValid(), "Coins are valid")
assert.True(t, good.IsPositive(), "Expected coins to be positive: %v", good) assert.True(t, good.IsPositive(), "Expected coins to be positive: %v", good)
assert.False(t, null.IsPositive(), "Expected coins to not be positive: %v", null) assert.False(t, null.IsPositive(), "Expected coins to not be positive: %v", null)
assert.True(t, good.IsGTE(empty), "Expected %v to be >= %v", good, empty) assert.True(t, good.IsAllGTE(empty), "Expected %v to be >= %v", good, empty)
assert.False(t, good.IsLT(empty), "Expected %v to be < %v", good, empty) assert.False(t, good.IsAllLT(empty), "Expected %v to be < %v", good, empty)
assert.True(t, empty.IsLT(good), "Expected %v to be < %v", empty, good) assert.True(t, empty.IsAllLT(good), "Expected %v to be < %v", empty, good)
assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg) assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg)
assert.Zero(t, len(sum), "Expected 0 coins") assert.Zero(t, len(sum), "Expected 0 coins")
assert.False(t, badSort1.IsValid(), "Coins are not sorted") assert.False(t, badSort1.IsValid(), "Coins are not sorted")
@ -257,6 +260,60 @@ func TestCoins(t *testing.T) {
} }
func TestCoinsGT(t *testing.T) {
one := NewInt(1)
two := NewInt(2)
assert.False(t, Coins{}.IsAllGT(Coins{}))
assert.True(t, Coins{{"A", one}}.IsAllGT(Coins{}))
assert.False(t, Coins{{"A", one}}.IsAllGT(Coins{{"A", one}}))
assert.False(t, Coins{{"A", one}}.IsAllGT(Coins{{"B", one}}))
assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllGT(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllGT(Coins{{"B", two}}))
}
func TestCoinsGTE(t *testing.T) {
one := NewInt(1)
two := NewInt(2)
assert.True(t, Coins{}.IsAllGTE(Coins{}))
assert.True(t, Coins{{"A", one}}.IsAllGTE(Coins{}))
assert.True(t, Coins{{"A", one}}.IsAllGTE(Coins{{"A", one}}))
assert.False(t, Coins{{"A", one}}.IsAllGTE(Coins{{"B", one}}))
assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllGTE(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllGTE(Coins{{"B", two}}))
}
func TestCoinsLT(t *testing.T) {
one := NewInt(1)
two := NewInt(2)
assert.False(t, Coins{}.IsAllLT(Coins{}))
assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{}))
assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{{"A", one}}))
assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"B", two}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"A", one}, {"B", one}}))
assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"A", one}, {"B", two}}))
assert.True(t, Coins{}.IsAllLT(Coins{{"A", one}}))
}
func TestCoinsLTE(t *testing.T) {
one := NewInt(1)
two := NewInt(2)
assert.True(t, Coins{}.IsAllLTE(Coins{}))
assert.False(t, Coins{{"A", one}}.IsAllLTE(Coins{}))
assert.True(t, Coins{{"A", one}}.IsAllLTE(Coins{{"A", one}}))
assert.False(t, Coins{{"A", one}}.IsAllLTE(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"B", one}}))
assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"B", two}}))
assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"A", one}, {"B", one}}))
assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"A", one}, {"B", two}}))
assert.True(t, Coins{}.IsAllLTE(Coins{{"A", one}}))
}
func TestPlusCoins(t *testing.T) { func TestPlusCoins(t *testing.T) {
one := NewInt(1) one := NewInt(1)
zero := NewInt(0) zero := NewInt(0)

105
types/config.go Normal file
View File

@ -0,0 +1,105 @@
package types
import (
"sync"
)
// Config is the structure that holds the SDK configuration parameters.
// This could be used to initialize certain configuration parameters for the SDK.
type Config struct {
mtx sync.RWMutex
sealed bool
bech32AddressPrefix map[string]string
}
var (
// Initializing an instance of Config
sdkConfig = &Config{
sealed: false,
bech32AddressPrefix: map[string]string{
"account_addr": Bech32PrefixAccAddr,
"validator_addr": Bech32PrefixValAddr,
"consensus_addr": Bech32PrefixConsAddr,
"account_pub": Bech32PrefixAccPub,
"validator_pub": Bech32PrefixValPub,
"consensus_pub": Bech32PrefixConsPub,
},
}
)
// GetConfig returns the config instance for the SDK.
func GetConfig() *Config {
return sdkConfig
}
func (config *Config) assertNotSealed() {
config.mtx.Lock()
defer config.mtx.Unlock()
if config.sealed {
panic("Config is sealed")
}
}
// SetBech32PrefixForAccount builds the Config with Bech32 addressPrefix and publKeyPrefix for accounts
// and returns the config instance
func (config *Config) SetBech32PrefixForAccount(addressPrefix, pubKeyPrefix string) {
config.assertNotSealed()
config.bech32AddressPrefix["account_addr"] = addressPrefix
config.bech32AddressPrefix["account_pub"] = pubKeyPrefix
}
// SetBech32PrefixForValidator builds the Config with Bech32 addressPrefix and publKeyPrefix for validators
// and returns the config instance
func (config *Config) SetBech32PrefixForValidator(addressPrefix, pubKeyPrefix string) {
config.assertNotSealed()
config.bech32AddressPrefix["validator_addr"] = addressPrefix
config.bech32AddressPrefix["validator_pub"] = pubKeyPrefix
}
// SetBech32PrefixForConsensusNode builds the Config with Bech32 addressPrefix and publKeyPrefix for consensus nodes
// and returns the config instance
func (config *Config) SetBech32PrefixForConsensusNode(addressPrefix, pubKeyPrefix string) {
config.assertNotSealed()
config.bech32AddressPrefix["consensus_addr"] = addressPrefix
config.bech32AddressPrefix["consensus_pub"] = pubKeyPrefix
}
// Seal seals the config such that the config state could not be modified further
func (config *Config) Seal() *Config {
config.mtx.Lock()
defer config.mtx.Unlock()
config.sealed = true
return config
}
// GetBech32AccountAddrPrefix returns the Bech32 prefix for account address
func (config *Config) GetBech32AccountAddrPrefix() string {
return config.bech32AddressPrefix["account_addr"]
}
// GetBech32ValidatorAddrPrefix returns the Bech32 prefix for validator address
func (config *Config) GetBech32ValidatorAddrPrefix() string {
return config.bech32AddressPrefix["validator_addr"]
}
// GetBech32ConsensusAddrPrefix returns the Bech32 prefix for consensus node address
func (config *Config) GetBech32ConsensusAddrPrefix() string {
return config.bech32AddressPrefix["consensus_addr"]
}
// GetBech32AccountPubPrefix returns the Bech32 prefix for account public key
func (config *Config) GetBech32AccountPubPrefix() string {
return config.bech32AddressPrefix["account_pub"]
}
// GetBech32ValidatorPubPrefix returns the Bech32 prefix for validator public key
func (config *Config) GetBech32ValidatorPubPrefix() string {
return config.bech32AddressPrefix["validator_pub"]
}
// GetBech32ConsensusPubPrefix returns the Bech32 prefix for consensus node public key
func (config *Config) GetBech32ConsensusPubPrefix() string {
return config.bech32AddressPrefix["consensus_pub"]
}

View File

@ -305,11 +305,11 @@ func TestSerializationGocodecJSON(t *testing.T) {
func TestSerializationGocodecBinary(t *testing.T) { func TestSerializationGocodecBinary(t *testing.T) {
d := mustNewDecFromStr(t, "0.333") d := mustNewDecFromStr(t, "0.333")
bz, err := cdc.MarshalBinary(d) bz, err := cdc.MarshalBinaryLengthPrefixed(d)
require.NoError(t, err) require.NoError(t, err)
var d2 Dec var d2 Dec
err = cdc.UnmarshalBinary(bz, &d2) err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2)
require.NoError(t, err) require.NoError(t, err)
require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2)
} }
@ -323,11 +323,11 @@ type testDEmbedStruct struct {
// TODO make work for UnmarshalJSON // TODO make work for UnmarshalJSON
func TestEmbeddedStructSerializationGocodec(t *testing.T) { func TestEmbeddedStructSerializationGocodec(t *testing.T) {
obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)}
bz, err := cdc.MarshalBinary(obj) bz, err := cdc.MarshalBinaryLengthPrefixed(obj)
require.Nil(t, err) require.Nil(t, err)
var obj2 testDEmbedStruct var obj2 testDEmbedStruct
err = cdc.UnmarshalBinary(bz, &obj2) err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, obj.Field1, obj2.Field1) require.Equal(t, obj.Field1, obj2.Field1)

View File

@ -64,7 +64,11 @@ type ValidatorSet interface {
func(index int64, validator Validator) (stop bool)) func(index int64, validator Validator) (stop bool))
// iterate through bonded validators by operator address, execute func for each validator // iterate through bonded validators by operator address, execute func for each validator
IterateValidatorsBonded(Context, IterateBondedValidatorsByPower(Context,
func(index int64, validator Validator) (stop bool))
// iterate through the consensus validator set of the last block by operator address, execute func for each validator
IterateLastValidators(Context,
func(index int64, validator Validator) (stop bool)) func(index int64, validator Validator) (stop bool))
Validator(Context, ValAddress) Validator // get a particular validator by operator address Validator(Context, ValAddress) Validator // get a particular validator by operator address
@ -113,7 +117,7 @@ type DelegationSet interface {
type StakingHooks interface { type StakingHooks interface {
OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created
OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes
OnValidatorRemoved(ctx Context, valAddr ValAddress) // Must be called when a validator is deleted OnValidatorRemoved(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is deleted
OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
@ -127,7 +128,7 @@ type KVStore interface {
// Has checks if a key exists. Panics on nil key. // Has checks if a key exists. Panics on nil key.
Has(key []byte) bool Has(key []byte) bool
// Set sets the key. Panics on nil key. // Set sets the key. Panics on nil key or value.
Set(key, value []byte) Set(key, value []byte)
// Delete deletes the key. Panics on nil key. // Delete deletes the key. Panics on nil key.
@ -176,6 +177,43 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator {
return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix))
} }
// Compare two KVstores, return either the first key/value pair
// at which they differ and whether or not they are equal, skipping
// value comparison for a set of provided prefixes
func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) {
iterA := a.Iterator(nil, nil)
iterB := b.Iterator(nil, nil)
count = int64(0)
for {
if !iterA.Valid() && !iterB.Valid() {
break
}
var kvA, kvB cmn.KVPair
if iterA.Valid() {
kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()}
iterA.Next()
}
if iterB.Valid() {
kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()}
iterB.Next()
}
compareValue := true
for _, prefix := range prefixesToSkip {
if bytes.Equal(kvA.Key[:len(prefix)], prefix) {
compareValue = false
}
}
if !bytes.Equal(kvA.Key, kvB.Key) {
return kvA, kvB, count, false
}
if compareValue && !bytes.Equal(kvA.Value, kvB.Value) {
return kvA, kvB, count, false
}
count++
}
return cmn.KVPair{}, cmn.KVPair{}, count, true
}
// CacheKVStore cache-wraps a KVStore. After calling .Write() on // CacheKVStore cache-wraps a KVStore. After calling .Write() on
// the CacheKVStore, all previously created CacheKVStores on the // the CacheKVStore, all previously created CacheKVStores on the
// object expire. // object expire.

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"encoding/binary"
"encoding/json" "encoding/json"
"time" "time"
@ -36,6 +37,13 @@ func MustSortJSON(toSortJSON []byte) []byte {
return js return js
} }
// Uint64ToBigEndian - marshals uint64 to a bigendian byte slice so it can be sorted
func Uint64ToBigEndian(i uint64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, i)
return b
}
// Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info // Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info
const SortableTimeFormat = "2006-01-02T15:04:05.000000000" const SortableTimeFormat = "2006-01-02T15:04:05.000000000"

View File

@ -93,16 +93,16 @@ func TestBaseAccountMarshal(t *testing.T) {
cdc := codec.New() cdc := codec.New()
codec.RegisterCrypto(cdc) codec.RegisterCrypto(cdc)
b, err := cdc.MarshalBinary(acc) b, err := cdc.MarshalBinaryLengthPrefixed(acc)
require.Nil(t, err) require.Nil(t, err)
acc2 := BaseAccount{} acc2 := BaseAccount{}
err = cdc.UnmarshalBinary(b, &acc2) err = cdc.UnmarshalBinaryLengthPrefixed(b, &acc2)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, acc, acc2) require.Equal(t, acc, acc2)
// error on bad bytes // error on bad bytes
acc2 = BaseAccount{} acc2 = BaseAccount{}
err = cdc.UnmarshalBinary(b[:len(b)/2], &acc2) err = cdc.UnmarshalBinaryLengthPrefixed(b[:len(b)/2], &acc2)
require.NotNil(t, err) require.NotNil(t, err)
} }

View File

@ -282,7 +282,8 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result {
// TODO: Make the gasPrice not a constant, and account for tx size. // TODO: Make the gasPrice not a constant, and account for tx size.
requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas) requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas)
if !ctx.MinimumFees().IsZero() && stdTx.Fee.Amount.IsLT(requiredFees) { // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B).
if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) {
// validators reject any tx from the mempool with less than the minimum fee per gas * gas factor // validators reject any tx from the mempool with less than the minimum fee per gas * gas factor
return sdk.ErrInsufficientFee(fmt.Sprintf( return sdk.ErrInsufficientFee(fmt.Sprintf(
"insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees)).Result() "insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees)).Result()

View File

@ -2,9 +2,9 @@ package cli
import ( import (
"fmt" "fmt"
"io/ioutil" "github.com/pkg/errors"
"github.com/spf13/viper" "github.com/spf13/viper"
"io/ioutil"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
@ -13,13 +13,14 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/spf13/cobra" "github.com/spf13/cobra"
amino "github.com/tendermint/go-amino" "github.com/tendermint/go-amino"
) )
const ( const (
flagAppend = "append" flagAppend = "append"
flagPrintSigs = "print-sigs" flagValidateSigs = "validate-signatures"
flagOffline = "offline" flagOffline = "offline"
flagSigOnly = "signature-only"
) )
// GetSignCommand returns the sign command // GetSignCommand returns the sign command
@ -30,6 +31,13 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm
Long: `Sign transactions created with the --generate-only flag. Long: `Sign transactions created with the --generate-only flag.
Read a transaction from <file>, sign it, and print its JSON encoding. Read a transaction from <file>, sign it, and print its JSON encoding.
If the flag --signature-only flag is on, it outputs a JSON representation
of the generated signature only.
If the flag --validate-signatures is on, then the command would check whether all required
signers have signed the transactions and whether the signatures were collected in the right
order.
The --offline flag makes sure that the client will not reach out to the local cache. The --offline flag makes sure that the client will not reach out to the local cache.
Thus account number or sequence number lookups will not be performed and it is Thus account number or sequence number lookups will not be performed and it is
recommended to set such parameters manually.`, recommended to set such parameters manually.`,
@ -37,8 +45,11 @@ recommended to set such parameters manually.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
} }
cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign")
cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") cmd.Flags().Bool(flagAppend, true,
cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") "Append the signature to the existing ones. If disabled, old signatures would be overwritten")
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.")
cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+
"those who have already signed it, and make sure that signatures are in the correct order.")
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.")
return cmd return cmd
} }
@ -50,25 +61,46 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.
return return
} }
if viper.GetBool(flagPrintSigs) { if viper.GetBool(flagValidateSigs) {
printSignatures(stdTx) if !printSignatures(stdTx) {
return fmt.Errorf("signatures validation failed")
}
return nil return nil
} }
name := viper.GetString(client.FlagName) name := viper.GetString(client.FlagName)
if name == "" {
return errors.New("required flag \"name\" has not been set")
}
cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder)
txBldr := authtxb.NewTxBuilderFromCLI() txBldr := authtxb.NewTxBuilderFromCLI()
newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, viper.GetBool(flagAppend), viper.GetBool(flagOffline)) // if --signature-only is on, then override --append
generateSignatureOnly := viper.GetBool(flagSigOnly)
appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly
newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, appendSig, viper.GetBool(flagOffline))
if err != nil { if err != nil {
return err return err
} }
var json []byte var json []byte
if cliCtx.Indent {
switch generateSignatureOnly {
case true:
switch cliCtx.Indent {
case true:
json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ")
default:
json, err = cdc.MarshalJSON(newTx.Signatures[0])
}
default:
switch cliCtx.Indent {
case true:
json, err = cdc.MarshalJSONIndent(newTx, "", " ") json, err = cdc.MarshalJSONIndent(newTx, "", " ")
} else { default:
json, err = cdc.MarshalJSON(newTx) json, err = cdc.MarshalJSON(newTx)
} }
}
if err != nil { if err != nil {
return err return err
} }
@ -77,17 +109,31 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.
} }
} }
func printSignatures(stdTx auth.StdTx) { func printSignatures(stdTx auth.StdTx) bool {
fmt.Println("Signers:") fmt.Println("Signers:")
for i, signer := range stdTx.GetSigners() { signers := stdTx.GetSigners()
for i, signer := range signers {
fmt.Printf(" %v: %v\n", i, signer.String()) fmt.Printf(" %v: %v\n", i, signer.String())
} }
sigs := stdTx.GetSignatures()
fmt.Println("") fmt.Println("")
fmt.Println("Signatures:") fmt.Println("Signatures:")
for i, sig := range stdTx.GetSignatures() { success := true
fmt.Printf(" %v: %v\n", i, sdk.AccAddress(sig.Address()).String()) if len(sigs) != len(signers) {
success = false
} }
return for i, sig := range stdTx.GetSignatures() {
sigAddr := sdk.AccAddress(sig.Address())
sigSanity := "OK"
if i >= len(signers) || !sigAddr.Equals(signers[i]) {
sigSanity = fmt.Sprintf("ERROR: signature %d does not match its respective signer", i)
success = false
}
fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity)
}
fmt.Println("")
return success
} }
func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) { func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) {

View File

@ -125,7 +125,8 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo))
return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo))
} }
// BuildAndSign builds a single message to be signed, and signs a transaction // BuildAndSign builds a single message to be signed, and signs a transaction
@ -166,7 +167,7 @@ func (bldr TxBuilder) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, erro
PubKey: info.GetPubKey(), PubKey: info.GetPubKey(),
}} }}
return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
} }
// SignStdTx appends a signature to a StdTx and returns a copy of a it. If append // SignStdTx appends a signature to a StdTx and returns a copy of a it. If append

View File

@ -36,12 +36,12 @@ func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
} }
feePool := &(sdk.Coins{}) feePool := &(sdk.Coins{})
fck.cdc.MustUnmarshalBinary(bz, feePool) fck.cdc.MustUnmarshalBinaryLengthPrefixed(bz, feePool)
return *feePool return *feePool
} }
func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) { func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) {
bz := fck.cdc.MustMarshalBinary(coins) bz := fck.cdc.MustMarshalBinaryLengthPrefixed(coins)
store := ctx.KVStore(fck.key) store := ctx.KVStore(fck.key)
store.Set(collectedFeesKey, bz) store.Set(collectedFeesKey, bz)
} }

33
x/auth/genesis.go Normal file
View File

@ -0,0 +1,33 @@
package auth
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all auth state that must be provided at genesis
type GenesisState struct {
CollectedFees sdk.Coins `json:"collected_fees"` // collected fees
}
// Create a new genesis state
func NewGenesisState(collectedFees sdk.Coins) GenesisState {
return GenesisState{
CollectedFees: collectedFees,
}
}
// Return a default genesis state
func DefaultGenesisState() GenesisState {
return NewGenesisState(sdk.Coins{})
}
// Init store state from genesis data
func InitGenesis(ctx sdk.Context, keeper FeeCollectionKeeper, data GenesisState) {
keeper.setCollectedFees(ctx, data.CollectedFees)
}
// ExportGenesis returns a GenesisState for a given context and keeper
func ExportGenesis(ctx sdk.Context, keeper FeeCollectionKeeper) GenesisState {
collectedFees := keeper.GetCollectedFees(ctx)
return NewGenesisState(collectedFees)
}

View File

@ -148,13 +148,13 @@ func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) int64 {
if bz == nil { if bz == nil {
accNumber = 0 accNumber = 0
} else { } else {
err := am.cdc.UnmarshalBinary(bz, &accNumber) err := am.cdc.UnmarshalBinaryLengthPrefixed(bz, &accNumber)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
bz = am.cdc.MustMarshalBinary(accNumber + 1) bz = am.cdc.MustMarshalBinaryLengthPrefixed(accNumber + 1)
store.Set(globalAccountNumberKey, bz) store.Set(globalAccountNumberKey, bz)
return accNumber return accNumber

View File

@ -3,15 +3,13 @@ package auth
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
codec "github.com/cosmos/cosmos-sdk/codec" codec "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
) )
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
@ -115,9 +113,92 @@ func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
acc := mapper.NewAccountWithAddress(ctx, addr) acc := mapper.NewAccountWithAddress(ctx, addr)
mapper.SetAccount(ctx, acc) mapper.SetAccount(ctx, acc)
} }
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
mapper.GetAccount(ctx, sdk.AccAddress(arr)) mapper.GetAccount(ctx, sdk.AccAddress(arr))
} }
} }
func BenchmarkAccountMapperGetAccountFoundWithCoins(b *testing.B) {
ms, capKey, _ := setupMultiStore()
cdc := codec.New()
RegisterBaseAccount(cdc)
// make context and mapper
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount)
coins := sdk.Coins{
sdk.NewCoin("LTC", sdk.NewInt(1000)),
sdk.NewCoin("BTC", sdk.NewInt(1000)),
sdk.NewCoin("ETH", sdk.NewInt(1000)),
sdk.NewCoin("XRP", sdk.NewInt(1000)),
sdk.NewCoin("BCH", sdk.NewInt(1000)),
sdk.NewCoin("EOS", sdk.NewInt(1000)),
}
// assumes b.N < 2**24
for i := 0; i < b.N; i++ {
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
addr := sdk.AccAddress(arr)
acc := mapper.NewAccountWithAddress(ctx, addr)
acc.SetCoins(coins)
mapper.SetAccount(ctx, acc)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
mapper.GetAccount(ctx, sdk.AccAddress(arr))
}
}
func BenchmarkAccountMapperSetAccount(b *testing.B) {
ms, capKey, _ := setupMultiStore()
cdc := codec.New()
RegisterBaseAccount(cdc)
// make context and mapper
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount)
b.ResetTimer()
// assumes b.N < 2**24
for i := 0; i < b.N; i++ {
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
addr := sdk.AccAddress(arr)
acc := mapper.NewAccountWithAddress(ctx, addr)
mapper.SetAccount(ctx, acc)
}
}
func BenchmarkAccountMapperSetAccountWithCoins(b *testing.B) {
ms, capKey, _ := setupMultiStore()
cdc := codec.New()
RegisterBaseAccount(cdc)
// make context and mapper
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount)
coins := sdk.Coins{
sdk.NewCoin("LTC", sdk.NewInt(1000)),
sdk.NewCoin("BTC", sdk.NewInt(1000)),
sdk.NewCoin("ETH", sdk.NewInt(1000)),
sdk.NewCoin("XRP", sdk.NewInt(1000)),
sdk.NewCoin("BCH", sdk.NewInt(1000)),
sdk.NewCoin("EOS", sdk.NewInt(1000)),
}
b.ResetTimer()
// assumes b.N < 2**24
for i := 0; i < b.N; i++ {
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
addr := sdk.AccAddress(arr)
acc := mapper.NewAccountWithAddress(ctx, addr)
acc.SetCoins(coins)
mapper.SetAccount(ctx, acc)
}
}

View File

@ -151,10 +151,11 @@ func DefaultTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
// StdTx.Msg is an interface. The concrete types // StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec // are registered by MakeTxCodec
err := cdc.UnmarshalBinary(txBytes, &tx) err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil { if err != nil {
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
} }
return tx, nil return tx, nil
} }
} }

View File

@ -25,7 +25,8 @@ in place of an input filename, the command reads from standard input.`,
if err != nil { if err != nil {
return return
} }
txBytes, err := cliCtx.Codec.MarshalBinary(stdTx)
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil { if err != nil {
return return
} }

View File

@ -59,7 +59,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command {
} }
// ensure account has enough coins // ensure account has enough coins
if !account.GetCoins().IsGTE(coins) { if !account.GetCoins().IsAllGTE(coins) {
return errors.Errorf("Address %s doesn't have enough coins to pay for this transaction.", from) return errors.Errorf("Address %s doesn't have enough coins to pay for this transaction.", from)
} }

View File

@ -22,11 +22,12 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht
return return
} }
txBytes, err := cliCtx.Codec.MarshalBinary(m.Tx) txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx)
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return return
} }
res, err := cliCtx.BroadcastTx(txBytes) res, err := cliCtx.BroadcastTx(txBytes)
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())

View File

@ -190,7 +190,7 @@ func setCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt s
// HasCoins returns whether or not an account has at least amt coins. // HasCoins returns whether or not an account has at least amt coins.
func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) bool { func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) bool {
ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins") ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins")
return getCoins(ctx, am, addr).IsGTE(amt) return getCoins(ctx, am, addr).IsAllGTE(amt)
} }
// SubtractCoins subtracts amt from the coins at the addr. // SubtractCoins subtracts amt from the coins at the addr.

View File

@ -14,7 +14,7 @@ import (
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances // NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant { func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant {
return func(app *baseapp.BaseApp, _ abci.Header) error { return func(app *baseapp.BaseApp) error {
ctx := app.NewContext(false, abci.Header{}) ctx := app.NewContext(false, abci.Header{})
accts := mock.GetAllAccounts(mapper, ctx) accts := mock.GetAllAccounts(mapper, ctx)
for _, acc := range accts { for _, acc := range accts {
@ -32,7 +32,7 @@ func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant
// TotalCoinsInvariant checks that the sum of the coins across all accounts // TotalCoinsInvariant checks that the sum of the coins across all accounts
// is what is expected // is what is expected
func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant { func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant {
return func(app *baseapp.BaseApp, _ abci.Header) error { return func(app *baseapp.BaseApp) error {
ctx := app.NewContext(false, abci.Header{}) ctx := app.NewContext(false, abci.Header{})
totalCoins := sdk.Coins{} totalCoins := sdk.Coins{}

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