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"
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:
<<: *defaults
parallelism: 1
@ -259,6 +277,9 @@ workflows:
- test_sim_gaia_fast:
requires:
- setup_dependencies
- test_sim_gaia_import_export:
requires:
- setup_dependencies
- test_sim_gaia_multi_seed:
requires:
- setup_dependencies

View File

@ -1,5 +1,62 @@
# 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
*October 24th, 2018*
@ -752,7 +809,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging)
BREAKING CHANGES
* [stake] MarshalJSON -> MarshalBinary
* [stake] MarshalJSON -> MarshalBinaryLengthPrefixed
* Queries against the store must be prefixed with the path "/store"
FEATURES

73
Gopkg.lock generated
View File

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

View File

@ -169,13 +169,22 @@ test_sim_gaia_nondeterminism:
test_sim_gaia_fast:
@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:
@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_COMMIT ?= true
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 \
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
build-linux build-docker-gaiadnode localnet-start localnet-stop \
format check-ledger test_sim_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
value := codec.Cdc.MustMarshalBinary(result)
value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result)
return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK),
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).
WithMinimumFees(app.minimumFees)
// Passes the rest of the path as an argument to the querier.
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
resBytes, err := querier(ctx, path[2:], req)

View File

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

View File

@ -10,9 +10,9 @@ import (
"strings"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
tmliteErr "github.com/tendermint/tendermint/lite/errors"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
@ -54,7 +54,7 @@ func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sd
return res, err
}
ctx.Codec.MustUnmarshalBinary(resRaw, &res)
ctx.Codec.MustUnmarshalBinaryLengthPrefixed(resRaw, &res)
return
}
@ -158,7 +158,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height,
Trusted: ctx.TrustNode,
Prove: !ctx.TrustNode,
}
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.
func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error {
func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) error {
if ctx.Verifier == nil {
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
}
var multiStoreProof store.MultiStoreProof
cdc := codec.New()
// TODO: Instead of reconstructing, stash on CLIContext field?
prt := store.DefaultProofRuntime()
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
// TODO: Better convention for path?
storeName, err := parseQueryStorePath(queryPath)
if err != nil {
return errors.Wrap(err, "failed to unmarshalBinary rangeProof")
return err
}
// verify the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(
multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash,
)
if err != nil {
return errors.Wrap(err, "failed in verifying the proof against appHash")
}
kp := merkle.KeyPath{}
kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
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 {
return errors.Wrap(err, "failed in the range proof verification")
return errors.Wrap(err, "failed to prove merkle proof")
}
return nil
@ -241,20 +238,40 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([
}
// 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 {
if !strings.HasPrefix(path, "/") {
return false
}
paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 {
switch {
case len(paths) != 3:
return false
}
if store.RequireProof("/" + paths[2]) {
case paths[0] != "store":
return false
case store.RequireProof("/" + paths[2]):
return true
}
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.
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) {

View File

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

View File

@ -17,7 +17,7 @@ func TestGenerateSelfSignedCert(t *testing.T) {
cert, err := x509.ParseCertificate(certBytes)
require.Nil(t, err)
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)
}

View File

@ -158,11 +158,11 @@ func TestNodeStatus(t *testing.T) {
res, body := Request(t, port, "GET", "/node_info", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var nodeInfo p2p.NodeInfo
var nodeInfo p2p.DefaultNodeInfo
err := cdc.UnmarshalJSON([]byte(body), &nodeInfo)
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
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.DeliverTx.Code)
var proposalID int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID)
var proposalID uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal
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.DeliverTx.Code)
var proposalID int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID)
var proposalID uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal
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.DeliverTx.Code)
var proposalID int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID)
var proposalID uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID)
// query proposal
proposal := getProposal(t, port, proposalID)
@ -732,18 +732,18 @@ func TestProposalsQuery(t *testing.T) {
// Addr1 proposes (and deposits) proposals #1 and #2
resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID1 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1)
var proposalID1 uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1)
tests.WaitForHeight(resultTx.Height+1, port)
resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
var proposalID2 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2)
var proposalID2 uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2)
tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 proposes (and deposits) proposals #3
resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5)
var proposalID3 int64
cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3)
var proposalID3 uint64
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3)
tests.WaitForHeight(resultTx.Height+1, port)
// Addr2 deposits on proposals #2 & #3
@ -1230,7 +1230,7 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA
// ============= 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)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var proposal gov.Proposal
@ -1239,7 +1239,7 @@ func getProposal(t *testing.T, port string, proposalID int64) gov.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)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var deposits []gov.Deposit
@ -1248,7 +1248,7 @@ func getDeposits(t *testing.T, port string, proposalID int64) []gov.Deposit {
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)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var deposit gov.Deposit
@ -1257,7 +1257,7 @@ func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.A
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)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var vote gov.Vote
@ -1266,7 +1266,7 @@ func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddre
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)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var votes []gov.Vote
@ -1358,7 +1358,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
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)
accnum := acc.GetAccountNumber()
@ -1388,7 +1388,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
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
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()

View File

@ -2,6 +2,7 @@ package lcd
import (
"errors"
"fmt"
"net"
"net/http"
"os"
@ -12,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
@ -21,7 +23,6 @@ import (
"github.com/rakyll/statik/fs"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
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) {
listenAddr := viper.GetString(flagListenAddr)
handler := createHandler(cdc)
registerSwaggerUI(handler)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
maxOpen := viper.GetInt(flagMaxOpenConnections)
sslHosts := viper.GetString(flagSSLHosts)
certFile := viper.GetString(flagSSLCertFile)
keyFile := viper.GetString(flagSSLKeyFile)
cleanupFunc := func() {}
var listener net.Listener
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) {
listener, err = tmserver.StartHTTPServer(
listenAddr, handler, logger,
tmserver.Config{MaxOpenConnections: maxOpen},
fmt.Println(
"Insecure mode is temporarily disabled, please locally generate an " +
"SSL certificate to test. Support will be re-enabled soon!",
)
if err != nil {
return
}
// listener, err = tmserver.StartHTTPServer(
// listenAddr, handler, logger,
// tmserver.Config{MaxOpenConnections: maxOpen},
// )
// if err != nil {
// return
// }
} else {
if certFile != "" {
// validateCertKeyFiles() is needed to work around tendermint/tendermint#2460
@ -72,6 +87,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil {
return err
}
// cert/key pair is provided, read the fingerprint
fingerprint, err = fingerprintFromFile(certFile)
if err != nil {
@ -83,12 +99,15 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil {
return err
}
cleanupFunc = func() {
os.Remove(certFile)
os.Remove(keyFile)
}
defer cleanupFunc()
}
listener, err = tmserver.StartHTTPAndTLSServer(
listenAddr, handler,
certFile, keyFile,
@ -98,16 +117,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
if err != nil {
return
}
logger.Info(fingerprint)
}
logger.Info("REST server started")
// wait forever and cleanup
cmn.TrapSignal(func() {
defer cleanupFunc()
err := listener.Close()
logger.Error("error closing listener", "err", err)
})
logger.Info(fingerprint)
logger.Info("REST server started")
}
// logger.Info("REST server started")
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().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")
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))

View File

@ -214,6 +214,7 @@ func InitializeTestLCD(
genTxs := []json.RawMessage{}
// append any additional (non-proposing) validators
var accs []gapp.GenesisAccount
for i := 0; i < nValidators; i++ {
operPrivKey := secp256k1.GenPrivKey()
operAddr := operPrivKey.PubKey().Address()
@ -242,9 +243,17 @@ func InitializeTestLCD(
genTxs = append(genTxs, txBytes)
valConsPubKeys = append(valConsPubKeys, pubKey)
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)
// 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) {
var tx auth.StdTx
err := cdc.UnmarshalBinary(txBytes, &tx)
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
return nil, err
}

View File

@ -65,6 +65,20 @@ func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok
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
// default value, defaultIfEmpty, if the string is empty.
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
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 {
@ -166,7 +167,7 @@ func adjustGasEstimate(estimate int64, adjustment float64) int64 {
func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
var simulationResult sdk.Result
if err := cdc.UnmarshalBinary(rawRes, &simulationResult); err != nil {
if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil {
return 0, err
}
return simulationResult.GasUsed, nil

View File

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

View File

@ -109,7 +109,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.cdc,
app.keyParams, app.tkeyParams,
)
app.stakeKeeper = stake.NewKeeper(
stakeKeeper := stake.NewKeeper(
app.cdc,
app.keyStake, app.tkeyStake,
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.paramsKeeper.Subspace(mint.DefaultParamspace),
app.stakeKeeper, app.feeCollectionKeeper,
&stakeKeeper, app.feeCollectionKeeper,
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
app.paramsKeeper.Subspace(distr.DefaultParamspace),
app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper,
app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper,
app.RegisterCodespace(stake.DefaultCodespace),
)
app.slashingKeeper = slashing.NewKeeper(
app.cdc,
app.keySlashing,
app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
&stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
app.RegisterCodespace(slashing.DefaultCodespace),
)
app.govKeeper = gov.NewKeeper(
app.cdc,
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),
)
// 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()))
// 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)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
// Add these new validators to the addr -> pubkey map.
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
@ -229,6 +228,10 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// 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
for _, gacc := range genesisState.Accounts {
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
}
// 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)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
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 {
panic(err)
}
bz := app.cdc.MustMarshalBinary(tx)
bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx)
res := app.BaseApp.DeliverTx(bz)
if !res.IsOK() {
panic(res.Log)
@ -268,12 +272,12 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
}
app.slashingKeeper.AddValidators(ctx, validators)
// sanity check
if len(req.Validators) > 0 {
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(validators))
@ -303,11 +307,12 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
app.accountKeeper.IterateAccounts(ctx, appendAccount)
genState := NewGenesisState(
accounts,
stake.WriteGenesis(ctx, app.stakeKeeper),
mint.WriteGenesis(ctx, app.mintKeeper),
distr.WriteGenesis(ctx, app.distrKeeper),
gov.WriteGenesis(ctx, app.govKeeper),
slashing.GenesisState{}, // TODO create write methods
auth.ExportGenesis(ctx, app.feeCollectionKeeper),
stake.ExportGenesis(ctx, app.stakeKeeper),
mint.ExportGenesis(ctx, app.mintKeeper),
distr.ExportGenesis(ctx, app.distrKeeper),
gov.ExportGenesis(ctx, app.govKeeper),
slashing.ExportGenesis(ctx, app.slashingKeeper),
)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
@ -334,12 +339,15 @@ var _ sdk.StakingHooks = Hooks{}
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, valAddr)
h.sh.OnValidatorCreated(ctx, valAddr)
}
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, valAddr)
h.sh.OnValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, valAddr)
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
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) {
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) {
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) {
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) {
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
freeFermionVal = int64(100)
freeFermionsAcc = sdk.NewInt(150)
bondDenom = "steak"
)
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
AuthData auth.GenesisState `json:"auth"`
StakeData stake.GenesisState `json:"stake"`
MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"`
@ -39,11 +41,12 @@ type GenesisState struct {
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 {
return GenesisState{
Accounts: accounts,
AuthData: authData,
StakeData: stakeData,
MintData: mintData,
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 {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence_number"`
AccountNumber int64 `json:"account_number"`
}
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
Coins: acc.Coins,
AccountNumber: acc.AccountNumber,
Sequence: acc.Sequence,
}
}
@ -69,6 +76,8 @@ func NewGenesisAccountI(acc auth.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
}
}
@ -77,6 +86,8 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
return &auth.BaseAccount{
Address: ga.Address,
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
// note that the pubkey input is this machines pubkey
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
if len(appGenTxs) == 0 {
err = errors.New("must provide at least genesis transaction")
return
func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
genesisState GenesisState, err error) {
if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil {
return genesisState, err
}
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
slashingData := slashing.DefaultGenesisState()
// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
// if there are no gen txs to be processed, return the default empty state
if len(appGenTxs) == 0 {
return genesisState, errors.New("there must be at least one genesis tx")
}
stakeData := genesisState.StakeData
for i, genTx := range appGenTxs {
var tx auth.StdTx
err = cdc.UnmarshalJSON(genTx, &tx)
if err != nil {
return
if err := cdc.UnmarshalJSON(genTx, &tx); err != nil {
return genesisState, err
}
msgs := tx.GetMsgs()
if len(msgs) != 1 {
err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message")
return
return genesisState, errors.New(
"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
genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply
for _, coin := range acc.Coins {
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
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
// NewDefaultGenesisState generates the default state for gaia.
func NewDefaultGenesisState() GenesisState {
return GenesisState{
Accounts: nil,
StakeData: stake.DefaultGenesisState(),
MintData: mint.DefaultGenesisState(),
DistrData: distr.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
SlashingData: slashingData,
GenTxs: appGenTxs,
SlashingData: slashing.DefaultGenesisState(),
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
@ -175,27 +188,43 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) {
}
// 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
genesisState, err := GaiaAppGenState(cdc, appGenTxs)
genesisState, err := GaiaAppGenState(cdc, genDoc, appGenTxs)
if err != nil {
return nil, err
}
appState, err = codec.MarshalJSONIndent(cdc, genesisState)
return
return codec.MarshalJSONIndent(cdc, genesisState)
}
// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators,
// appGenTxs, and persistent peers required to generate genesis.json.
func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) (
validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) {
// CollectStdTxs processes and validates application's genesis StdTxs and returns
// the list of appGenTxs, and persistent peers required to generate genesis.json.
func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) (
appGenTxs []auth.StdTx, persistentPeers string, err error) {
var fos []os.FileInfo
fos, err = ioutil.ReadDir(genTxsDir)
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 {
filename := filepath.Join(genTxsDir, fo.Name())
if !fo.IsDir() && (filepath.Ext(filename) != ".json") {
@ -204,48 +233,55 @@ func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) (
// get the genStdTx
var jsonRawTx []byte
jsonRawTx, err = ioutil.ReadFile(filename)
if err != nil {
return
if jsonRawTx, err = ioutil.ReadFile(filename); err != nil {
return appGenTxs, persistentPeers, err
}
var genStdTx auth.StdTx
err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx)
if err != nil {
return
if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil {
return appGenTxs, persistentPeers, err
}
appGenTxs = append(appGenTxs, genStdTx)
nodeAddr := genStdTx.GetMemo()
if len(nodeAddr) == 0 {
err = fmt.Errorf("couldn't find node's address in %s", fo.Name())
return
// the memo flag is used to store
// the ip and node-id, for example this may be:
// "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656"
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()
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
// TODO: and we likely want to do it for real world Gaia
// validate the validator address and funds against the accounts in the state
msg := msgs[0].(stake.MsgCreateValidator)
validators = append(validators, tmtypes.GenesisValidator{
PubKey: msg.PubKey,
Power: freeFermionVal,
Name: msg.Description.Moniker,
})
addr := string(sdk.AccAddress(msg.ValidatorAddr))
acc, ok := addrMap[addr]
if !ok {
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
if msg.Description.Moniker != moniker {
addresses = append(addresses, nodeAddr)
addressesIPs = append(addressesIPs, nodeAddrIP)
}
}
sort.Strings(addresses)
persistentPeers = strings.Join(addresses, ",")
sort.Strings(addressesIPs)
persistentPeers = strings.Join(addressesIPs, ",")
return
return appGenTxs, persistentPeers, nil
}
func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount {

View File

@ -1,11 +1,14 @@
package app
import (
"encoding/json"
"testing"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake"
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/stretchr/testify/require"
@ -27,7 +30,8 @@ var (
func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
appState := NewDefaultGenesisState()
stakeData := appState.StakeData
genAccs := make([]GenesisAccount, len(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))
msg := msgs[0].(stake.MsgCreateValidator)
// get genesis flag account information
genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply
acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
acc.Coins = sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)}
genAccs[i] = NewGenesisAccount(&acc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(150)) // increase the supply
}
// create the final app state
return GenesisState{
Accounts: genAccs,
StakeData: stakeData,
GovData: gov.DefaultGenesisState(),
}
appState.Accounts = genAccs
return appState
}
func TestToAccount(t *testing.T) {
@ -68,6 +70,19 @@ func TestGaiaAppGenTx(t *testing.T) {
func TestGaiaAppGenState(t *testing.T) {
cdc := MakeCodec()
_ = 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 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 {
desc := stake.NewDescription(name, "", "", "")
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, "")
}
@ -106,3 +124,10 @@ func TestGaiaGenesisValidation(t *testing.T) {
err = GaiaValidateGenesisState(genesisState)
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"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"os"
"testing"
"time"
"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"
@ -49,42 +52,93 @@ func init() {
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
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
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{
Address: acc.Address,
Coins: coins,
})
}
// Default genesis state
govGenesis := gov.DefaultGenesisState()
stakeGenesis := stake.DefaultGenesisState()
slashingGenesis := slashing.DefaultGenesisState()
// Random genesis states
govGenesis := gov.GenesisState{
StartingProposalID: uint64(r.Intn(100)),
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 delegations []stake.Delegation
// XXX Try different numbers of initially bonded validators
numInitiallyBonded := int64(50)
valAddrs := make([]sdk.ValAddress, numInitiallyBonded)
for i := 0; i < int(numInitiallyBonded); i++ {
valAddr := sdk.ValAddress(accs[i].Address)
valAddrs[i] = valAddr
validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{})
validator.Tokens = sdk.NewDec(amt)
validator.DelegatorShares = sdk.NewDec(amt)
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0}
validator.Tokens = sdk.NewDec(amount)
validator.DelegatorShares = sdk.NewDec(amount)
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0}
validators = append(validators, validator)
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.Bonds = delegations
mintGenesis := mint.DefaultGenesisState()
genesis := GenesisState{
Accounts: genesisAccounts,
@ -113,7 +167,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
{50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)},
{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)},
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
{100, stakesim.SimulateMsgDelegate(app.accountKeeper, app.stakeKeeper)},
@ -141,7 +195,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
var logger log.Logger
logger = log.NewNopLogger()
var db dbm.DB
dir := os.TempDir()
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
defer func() {
db.Close()
@ -183,7 +237,13 @@ func TestFullGaiaSimulation(t *testing.T) {
} else {
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)
require.Equal(t, "GaiaApp", app.Name())
@ -198,11 +258,112 @@ func TestFullGaiaSimulation(t *testing.T) {
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)
}
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
// and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) {

View File

@ -8,8 +8,11 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
@ -207,7 +210,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
func TestGaiaCLICreateValidator(t *testing.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
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))
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))
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())
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())
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))
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))
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
voteStr := fmt.Sprintf("gaiacli tx vote %v", flags)
@ -405,12 +414,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
tests.WaitForNextNBlocksTM(2, port)
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)
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags))
require.Len(t, votes, 1)
require.Equal(t, int64(1), votes[0].ProposalID)
require.Equal(t, uint64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option)
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)
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)
}
@ -439,7 +448,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
// 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)
tests.WaitForTMStart(port)
@ -484,11 +494,11 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
unsignedTxFile := writeToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())
// Test sign --print-sigs
// Test sign --validate-signatures
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name()))
require.True(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout)
"gaiacli tx sign %v --validate-signatures %v", flags, unsignedTxFile.Name()))
require.False(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
// Test sign
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
@ -505,15 +515,17 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
// Test sign --print-signatures
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.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
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
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)
var result struct {
Response abci.ResponseDeliverTx
@ -535,7 +547,7 @@ func TestGaiaCLIConfig(t *testing.T) {
servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
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")
config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
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), "")
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
chainID = executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome))
executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s foo", 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
servAddr, port, err := server.FreeTCPAddr()
servAddr, port, err = server.FreeTCPAddr()
require.NoError(t, err)
return
}
@ -609,6 +636,18 @@ func writeToNewTempFile(t *testing.T, s string) *os.File {
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
@ -693,6 +732,24 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.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 {
out, _ := tests.ExecuteT(t, cmdStr, "")
var pool stake.Pool

View File

@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
@ -32,6 +33,7 @@ const (
storeGov = "gov"
storeSlashing = "slashing"
storeStake = "stake"
queryRouteStake = "stake"
)
// rootCmd is the entry point for this binary
@ -46,6 +48,12 @@ func main() {
cobra.EnableCommandSorting = false
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
// the below functions and eliminate global vars, like we do
// with the cdc
@ -70,21 +78,23 @@ func main() {
authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryDelegation(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.GetCmdQueryPool(storeStake, cdc),
govcmd.GetCmdQueryProposal(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.GetCmdQueryVotes(storeGov, cdc),
govcmd.GetCmdQueryDeposit(storeGov, cdc),
govcmd.GetCmdQueryDeposits(storeGov, cdc),
slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc),
)...)
//Add query commands

View File

@ -18,10 +18,18 @@ import (
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func main() {
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()
cobra.EnableCommandSorting = false
rootCmd := &cobra.Command{
@ -31,7 +39,8 @@ func main() {
}
appInit := app.GaiaAppInit()
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))
server.AddCommands(ctx, cdc, rootCmd, appInit,

View File

@ -19,6 +19,13 @@ import (
)
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(pubkeyCmd)
rootCmd.AddCommand(addrCmd)
@ -213,7 +220,7 @@ func runTxCmd(cmd *cobra.Command, args []string) error {
var tx = auth.StdTx{}
cdc := gaia.MakeCodec()
err = cdc.UnmarshalBinary(txBytes, &tx)
err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
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 {
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
prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey)
prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey)
createValidatorCmd := cli.GetCmdCreateValidator(cdc)
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(client.FlagChainID, "", "genesis file chain-id")
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)
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(client.FlagChainID, chainID)
viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from
viper.Set(cli.FlagNodeID, nodeID) // --node-id
viper.Set(cli.FlagIP, ip) // --ip
viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey
viper.Set(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.FlagMoniker, config.Moniker) // --moniker
if config.Moniker == "" {
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() {

View File

@ -2,51 +2,27 @@ package init
import (
"encoding/json"
"errors"
"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"
"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"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
const (
flagWithTxs = "with-txs"
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagOverwriteKey = "overwrite-key"
flagSkipGenesis = "skip-genesis"
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 {
Moniker string `json:"moniker"`
ChainID string `json:"chain_id"`
@ -70,22 +46,16 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
cmd := &cobra.Command{
Use: "init",
Short: "Initialize private validator, p2p, genesis, and application 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.
`,
Long: `Initialize validators's and node's configuration files.`,
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName)
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
nodeID, _, err := InitializeNodeValidatorFiles(config)
if err != nil {
return err
}
@ -93,37 +63,26 @@ enabled, and the genesis file will not be generated.
if 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{
ChainID: chainID,
Moniker: config.Moniker,
NodeID: nodeID,
AppMessage: appState,
}
if viper.GetBool(flagSkipGenesis) {
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)
},
}
@ -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().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().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/")
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")
cmd.Flags().String(flagMoniker, "", "set the validator's moniker")
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")
require.Nil(t, err)
viper.Set(flagClientHome, clientDir)
viper.Set(flagOverwriteKey, true)
return func() {
if err := os.RemoveAll(clientDir); err != nil {
// TODO: Handle with #870

View File

@ -3,6 +3,10 @@ package init
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
@ -10,9 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
@ -20,22 +21,25 @@ import (
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
var (
nodeDirPrefix = "node-dir-prefix"
nValidators = "v"
outputDir = "output-dir"
nodeDaemonHome = "node-daemon-home"
nodeCliHome = "node-cli-home"
startingIPAddress = "starting-ip-address"
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "v"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
flagNodeCliHome = "node-cli-home"
flagStartingIPAddress = "starting-ip-address"
)
const nodeDirPerm = 0755
// 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{
Use: "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.
Example:
gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
`,
RunE: func(_ *cobra.Command, _ []string) error {
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, ...)")
return cmd
}
func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppInit) error {
outDir := viper.GetString(outputDir)
numValidators := viper.GetInt(nValidators)
func initTestnet(config *cfg.Config, cdc *codec.Codec) error {
outDir := viper.GetString(flagOutputDir)
numValidators := viper.GetInt(flagNumValidators)
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6)
monikers := make([]string, numValidators)
nodeIDs := make([]string, 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++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeCliHomeName := viper.GetString(nodeCliHome)
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome)
nodeCliHomeName := viper.GetString(flagNodeCliHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
config.SetRoot(nodeDir)
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)
config.Moniker = nodeDirName
ip, err := getIP(i)
ip, err := getIP(i, viper.GetString(flagStartingIPAddress))
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())
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)
if err != nil && keyPass != "" {
// 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.
return err
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
@ -133,17 +156,28 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
_ = os.RemoveAll(outDir)
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// Save private key seed words
// save private key seed words
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil {
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(
sdk.ValAddress(addr),
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)
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo)
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
if err != nil {
_ = os.RemoveAll(outDir)
@ -165,7 +200,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
return err
}
// Gather gentxs folder
// gather gentxs folder
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil {
_ = os.RemoveAll(outDir)
@ -173,64 +208,140 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI
}
}
for i := 0; i < numValidators; i++ {
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 := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil {
return err
}
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
}
}
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
return nil
}
func getIP(i int) (ip string, err error) {
ip = viper.GetString(startingIPAddress)
if len(ip) == 0 {
func collectGenFiles(
cdc *codec.Codec, config *cfg.Config, chainID string,
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()
if err != nil {
return "", err
}
} else {
ip, err = calculateIP(ip, i)
ip, err = calculateIP(startingIPAddr, i)
if err != nil {
return "", err
}
}
return ip, nil
}
func writeFile(name string, dir string, contents []byte) error {
writePath := filepath.Join(dir)
file := filepath.Join(writePath, name)
err := cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, contents, 0600)
if err != nil {
return err
}
return nil
}
@ -243,5 +354,6 @@ func calculateIP(ip string, i int) (string, error) {
for j := 0; j < i; j++ {
ipv4[3]++
}
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 {
return nil, nil, err
}
cdc.MustUnmarshalBinary([]byte(signed), sig)
cdc.MustUnmarshalBinaryLengthPrefixed([]byte(signed), sig)
return sig, linfo.GetPubKey(), nil
}
sig, err = priv.Sign(msg)

View File

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

View File

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

View File

@ -36,6 +36,8 @@
- Tags [#1780](https://github.com/cosmos/cosmos-sdk/issues/1780)
# Lower priority
- Create some diagrams (see `docs/resources/diagrams/todo.md`)
## Governance v2
- 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
```
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:
```
@ -285,7 +291,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va
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
@ -321,7 +333,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va
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

View File

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

View File

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

View File

@ -39,7 +39,7 @@ func TestEncoding(t *testing.T) {
cdc := NewCodec()
testTxDecoder := tx2Decoder(cdc)
encodedSendTx, err := cdc.MarshalBinary(sendTxBefore)
encodedSendTx, err := cdc.MarshalBinaryLengthPrefixed(sendTxBefore)
require.Nil(t, err, "Error encoding sendTx")
@ -69,7 +69,7 @@ func TestEncoding(t *testing.T) {
Signature: sig,
}
encodedIssueTx, err2 := cdc.MarshalBinary(issueTxBefore)
encodedIssueTx, err2 := cdc.MarshalBinaryLengthPrefixed(issueTxBefore)
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.
The requirements for this vesting account is that it should be initialized during genesis with
a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators
and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked.
The vesting account should also be able to spend any coins it receives from other users.
Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their
unlocked coin amount.
The requirements for this vesting account is that it should be initialized
during genesis with a starting balance `X` coins and a vesting end time `T`.
### 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
// VestingAccount defines an interface that any vesting account type must
// implement.
type VestingAccount interface {
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
SendableCoins(sdk.Context) sdk.Coins
// Calculates the amount of coins that can be sent to other accounts given
// the current time.
SpendableCoins(Context) Coins
// Performs delegation accounting.
TrackDelegation(amount)
// Performs undelegation accounting.
TrackUndelegation(amount)
}
// Implements Vesting Account
// Continuously vests by unlocking coins linearly with respect to time
// BaseVestingAccount implements the VestingAccount interface. It contains all
// 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 {
BaseAccount
OriginalVestingCoins sdk.Coins // Coins in account on Initialization
ReceivedCoins sdk.Coins // Coins received from other accounts
SentCoins sdk.Coins // Coins sent to other accounts
BaseVestingAccount
// StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point
StartTime time.Time
EndTime time.Time
DelegatedVesting Coins // coins that vesting and delegated
StartTime Time // when the coins start to vest
}
// Uses time in context to calculate total unlocked coins
SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins:
// Coins unlocked by vesting schedule
unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)
// Must still check for currentCoins constraint since some unlocked coins may have been delegated.
currentCoins := vacc.BaseAccount.GetCoins()
// min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins
return min(unlockedCoins, currentCoins)
// DelayedVestingAccount implements the VestingAccount interface. It vests all
// coins after a specific time, but non prior. In other words, it keeps them
// locked until a specified time.
type DelayedVestingAccount struct {
BaseAccount
BaseVestingAccount
}
```
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
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`,
`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point.
Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements
the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of
both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period.
##### Changes to Keepers/Handler
Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be
handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like
`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above.
Delayed vesting accounts are easier to reason about as they only have the full
amount vesting up until a certain time, then they all become vested (unlocked).
```go
if acc is VestingAccount and Now < vestingAccount.EndTime:
// Check if amount is less than currently allowed sendable coins
if msg.Amount > vestingAccount.SendableCoins(ctx) then fail
else:
vestingAccount.SentCoins += msg.Amount
func (dva DelayedVestingAccount) GetVestedCoins(b Block) Coins {
if b.Time >= dva.EndTime {
return dva.OriginalVesting
}
else:
// Account has fully vested, treat like regular account
if msg.Amount > account.GetCoins() then fail
// All checks passed, send the coins
SendCoins(inputs, outputs)
return ZeroCoins
}
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
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.
### Transferring/Sending
CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`.
However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented.
#### Continuously Vesting Accounts
### 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
BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts
as appropriate.
In other words, a vesting account may transfer the minimum of the base account
balance and the base account balance plus the number of currently delegated
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
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
GenesisCoins sdk.Coins `json:"coins"`
EndTime int64 `json:"lock"`
Address sdk.AccAddress
GenesisCoins sdk.Coins
EndTime int64
}
initChainer:
for gacc in GenesisAccounts:
func initChainer() {
for genAcc in GenesisAccounts {
baseAccount := BaseAccount{
Address: gacc.Address,
Coins: gacc.GenesisCoins,
Address: genAcc.Address,
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:
`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent`
`CurrentCoins = vestingAccount.GetCoins()`
**Maximum amount of coins spendable right now:**
`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )`
- 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.
- 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.
- 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.
- DelayedVestingAccount: A vesting account implementation that only fully vests all coins at a given time.

View File

@ -97,9 +97,11 @@ type Proposal struct {
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
Deposits []Deposit // List of deposits on the proposal
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
VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end.
CurrentStatus ProposalStatus // Current status of the proposal
YesVotes sdk.Dec
@ -134,46 +136,26 @@ For pseudocode purposes, here are the two function we will use to read or write
**Store:**
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if
`CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is,
then the application tallies the votes, compute the votes of 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.
After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated.
`ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals
that have reached the end of their voting period are processed.
To process a finished proposal, the application tallies the votes, compute the votes of
each validator and checks if every validator in the valdiator set have voted.
If the proposal is accepted, deposits are refunded.
And the pseudocode for the `ProposalProcessingQueue`:
```go
in EndBlock do
checkProposal() // First call of the recursive function
// Recursive function. First call in BeginBlock
func checkProposal()
proposalID = ProposalProcessingQueue.Peek()
if (proposalID == nil)
return
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
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
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
// Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
for each validator in validators
tmpValMap(validator).Minus = 0
tmpValMap(validator.OperatorAddr).Minus = 0
// Tally
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
store(Governance, <proposalID|'proposal'>, proposal)
checkProposal()
```

View File

@ -46,6 +46,8 @@ upon receiving txGovSubmitProposal from sender do
sender.AtomBalance -= initialDeposit.Atoms
depositProcedure = load(GlobalParams, 'DepositProcedure')
proposalID = generate new proposalID
proposal = NewProposal()
@ -53,28 +55,16 @@ upon receiving txGovSubmitProposal from sender do
proposal.Description = txGovSubmitProposal.Description
proposal.Type = txGovSubmitProposal.Type
proposal.TotalDeposit = initialDeposit
proposal.SubmitBlock = CurrentBlock
proposal.SubmitTime = <CurrentTime>
proposal.DepositEndTime = <CurrentTime>.Add(depositProcedure.MaxDepositPeriod)
proposal.Deposits.append({initialDeposit, sender})
proposal.Submitter = sender
proposal.YesVotes = 0
proposal.NoVotes = 0
proposal.NoWithVetoVotes = 0
proposal.AbstainVotes = 0
depositProcedure = load(GlobalParams, 'DepositProcedure')
if (initialDeposit < depositProcedure.MinDeposit)
// MinDeposit is not reached
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
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 {
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
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
OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding

View File

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

View File

@ -41,7 +41,6 @@ func main() {
appInit := server.DefaultAppInit
rootCmd.AddCommand(InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
server.AddCommands(ctx, cdc, rootCmd, appInit,
newApp, exportAppStateAndTMValidators)
@ -85,7 +84,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err
}
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx})
appState, err := appInit.AppGenState(
cdc, tmtypes.GenesisDoc{}, []json.RawMessage{genTx})
if err != nil {
return err
}
@ -108,13 +108,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err
}
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(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.MarkFlagRequired(client.FlagName)
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")))
}
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)
return bapp.ExportAppStateAndValidators()
}

View File

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

View File

@ -21,6 +21,8 @@ import (
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/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"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// rootCmd is the entry point for this binary
@ -38,6 +40,13 @@ func main() {
// get the codec
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
// the below functions and eliminate global vars, like we do
// with the cdc

View File

@ -23,6 +23,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
@ -35,8 +36,9 @@ var CoolAppInit = server.AppInit{
}
// 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) {
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, tmtypes.GenesisDoc{}, appGenTxs)
if err != nil {
return
}
@ -89,7 +91,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err
}
appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx})
appState, err := appInit.AppGenState(cdc, tmtypes.GenesisDoc{},
[]json.RawMessage{genTx})
if err != nil {
return err
}
@ -112,13 +115,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
return err
}
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(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.MarkFlagRequired(client.FlagName)
return cmd
@ -128,13 +133,22 @@ func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application {
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)
return dapp.ExportAppStateAndValidators()
}
func main() {
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()
rootCmd := &cobra.Command{

View File

@ -82,8 +82,13 @@ func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64,
}
}
// IterateValidatorsBonded implements sdk.ValidatorSet
func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
// IterateBondedValidatorsByPower implements sdk.ValidatorSet
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)
}

View File

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

View File

@ -37,7 +37,7 @@ func TestCoolKeeper(t *testing.T) {
err := InitGenesis(ctx, keeper, Genesis{"icy"})
require.Nil(t, err)
genesis := WriteGenesis(ctx, keeper)
genesis := ExportGenesis(ctx, keeper)
require.Nil(t, err)
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 {
return EmptyInfo(ctx)
}
keeper.cdc.MustUnmarshalBinary(bz, &res)
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
return
}
@ -80,7 +80,7 @@ func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) {
store := ctx.KVStore(keeper.key)
key := GetInfoKey(p, keeper.cdc)
bz := keeper.cdc.MustMarshalBinary(info)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(info)
store.Set(key, bz)
}

View File

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

View File

@ -82,7 +82,7 @@ func getSequence(ctx sdk.Context, key sdk.StoreKey) int {
if seqbz == nil {
seq = 0
} else {
codec.New().MustUnmarshalBinary(seqbz, &seq)
codec.New().MustUnmarshalBinaryLengthPrefixed(seqbz, &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, "")
}
bz := codec.New().MustMarshalBinary(seq + 1)
bz := codec.New().MustMarshalBinaryLengthPrefixed(seq + 1)
store.Set([]byte("seq"), bz)
return nil

View File

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

View File

@ -41,7 +41,7 @@ func TestPowKeeperGetSet(t *testing.T) {
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
require.Nil(t, err)
genesis := WriteGenesis(ctx, keeper)
genesis := ExportGenesis(ctx, keeper)
require.Nil(t, err)
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{}
}
var bi bondInfo
err := k.cdc.UnmarshalBinary(bz, &bi)
err := k.cdc.UnmarshalBinaryLengthPrefixed(bz, &bi)
if err != nil {
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) {
store := ctx.KVStore(k.key)
bz, err := k.cdc.MarshalBinary(bi)
bz, err := k.cdc.MarshalBinaryLengthPrefixed(bi)
if err != nil {
panic(err)
}

View File

@ -1,6 +1,7 @@
#!/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
echo "Running multi-seed simulation with seeds ${seeds[@]}"

View File

@ -19,7 +19,8 @@ import (
type AppInit struct {
// AppGenState creates the core parameters initialization. It takes in a
// 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
@ -35,7 +36,8 @@ var DefaultAppInit = AppInit{
}
// 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 secret string
addr, secret, err = GenerateCoinKey()
@ -63,7 +65,8 @@ func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json
}
// 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 {
err = errors.New("must provide a single genesis transaction")

View File

@ -3,6 +3,7 @@ package mock
import (
"encoding/json"
"fmt"
"github.com/tendermint/tendermint/types"
"path/filepath"
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
// 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(`{
"values": [
{
@ -119,7 +121,8 @@ func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage,
}
// 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(``)
return
}

View File

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

View File

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

View File

@ -4,7 +4,9 @@ import (
"encoding/json"
"net"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -203,6 +205,23 @@ func ExternalIP() (string, error) {
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 {
if iface.Flags&net.FlagUp == 0 {
return true // interface down

View File

@ -61,6 +61,7 @@ func (ci *cacheKVStore) Set(key []byte, value []byte) {
ci.mtx.Lock()
defer ci.mtx.Unlock()
ci.assertValidKey(key)
ci.assertValidValue(value)
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.
func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) {
ci.cache[string(key)] = cValue{

View File

@ -5,9 +5,9 @@ import (
"io"
"sync"
"github.com/tendermint/go-amino"
"github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
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)
switch req.Path {
case "/store", "/key": // Get by key
key := req.Data // Data holds the key bytes
case "/key": // get by key
key := req.Data // data holds the key bytes
res.Key = key
if !st.VersionExists(res.Height) {
res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error()
break
}
if req.Prove {
value, proof, err := tree.GetVersionedWithProof(key, res.Height)
if err != nil {
res.Log = err.Error()
break
}
res.Value = value
cdc := amino.NewCodec()
p, err := cdc.MarshalBinary(proof)
if err != nil {
res.Log = err.Error()
break
if proof == nil {
// Proof == nil implies that the store is empty.
if value != nil {
panic("unexpected value for an empty proof")
}
}
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 {
_, res.Value = tree.GetVersioned(key, res.Height)
}
case "/subspace":
var KVs []KVPair
subspace := req.Data
res.Key = subspace
var KVs []KVPair
iterator := sdk.KVStorePrefixIterator(st, subspace)
for ; iterator.Valid(); iterator.Next() {
KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()})
}
iterator.Close()
res.Value = cdc.MustMarshalBinary(KVs)
res.Value = cdc.MustMarshalBinaryLengthPrefixed(KVs)
default:
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
return sdk.ErrUnknownRequest(msg).QueryResult()
}
return
}

View File

@ -394,9 +394,9 @@ func TestIAVLStoreQuery(t *testing.T) {
{Key: k1, Value: v3},
{Key: k2, Value: v2},
}
valExpSubEmpty := cdc.MustMarshalBinary(KVs0)
valExpSub1 := cdc.MustMarshalBinary(KVs1)
valExpSub2 := cdc.MustMarshalBinary(KVs2)
valExpSubEmpty := cdc.MustMarshalBinaryLengthPrefixed(KVs0)
valExpSub1 := cdc.MustMarshalBinaryLengthPrefixed(KVs1)
valExpSub2 := cdc.MustMarshalBinaryLengthPrefixed(KVs2)
cid := iavlStore.Commit()
ver := cid.Version
@ -459,7 +459,7 @@ func TestIAVLStoreQuery(t *testing.T) {
require.Equal(t, valExpSub2, qres.Value)
// 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)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v1, qres.Value)

View File

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

View File

@ -2,90 +2,139 @@ package store
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
)
// MultiStoreProof defines a collection of store proofs in a multi-store
type MultiStoreProof struct {
StoreInfos []storeInfo
StoreName string
RangeProof iavl.RangeProof
}
// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos
func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte {
var rangeProof iavl.RangeProof
cdc.MustUnmarshalBinary(iavlProof, &rangeProof)
msp := MultiStoreProof{
StoreInfos: storeInfos,
StoreName: storeName,
RangeProof: rangeProof,
}
proof := cdc.MustMarshalBinary(msp)
return proof
func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof {
return &MultiStoreProof{StoreInfos: storeInfos}
}
// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash
func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) {
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")
}
// ComputeRootHash returns the root hash for a given multi-store proof.
func (proof *MultiStoreProof) ComputeRootHash() []byte {
ci := commitInfo{
Version: height,
StoreInfos: storeInfos,
Version: -1, // TODO: Not needed; improve code.
StoreInfos: proof.StoreInfos,
}
if !bytes.Equal(appHash, ci.Hash()) {
return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash")
}
return substoreCommitHash, nil
return ci.Hash()
}
// VerifyRangeProof verify iavl RangeProof
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
// RequireProof returns whether proof is required for the subpath.
func RequireProof(subpath string) bool {
// Currently, only when query subpath is "/store" or "/key", will proof be included in response.
// If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212
if subpath == "/store" || subpath == "/key" {
// XXX: create a better convention.
// Currently, only when query subpath is "/key", will proof be included in
// 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 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
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/db"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
)
func TestVerifyMultiStoreCommitInfo(t *testing.T) {
appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2")
substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
storeName := "acc"
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)
func TestVerifyIAVLStoreQueryProof(t *testing.T) {
// Create main tree for testing.
db := dbm.NewMemDB()
iStore, err := LoadIAVLStore(db, CommitID{}, sdk.PruneNothing)
store := iStore.(*iavlStore)
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)
require.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo")
// Verify proof.
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) {
tree := iavl.NewMutableTree(db.NewMemDB(), 0)
func TestVerifyMultiStoreQueryProof(t *testing.T) {
// Create main tree for testing.
db := dbm.NewMemDB()
store := NewCommitMultiStore(db)
iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey")
rand := cmn.NewRand()
rand.Seed(0) // for determinism
for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} {
key := []byte{ikey}
tree.Set(key, []byte(rand.Str(8)))
}
store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil)
store.LoadVersion(0)
root := tree.WorkingHash()
iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore)
iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE"))
cid := store.Commit()
key := []byte{0x32}
val, proof, err := tree.GetWithProof(key)
assert.Nil(t, err)
assert.NotEmpty(t, val)
assert.NotEmpty(t, proof)
err = VerifyRangeProof(key, val, root, proof)
assert.Nil(t, err)
// 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)
key = []byte{0x40}
val, proof, err = tree.GetWithProof(key)
assert.Nil(t, err)
assert.Empty(t, val)
assert.NotEmpty(t, proof)
err = VerifyRangeProof(key, val, root, proof)
assert.Nil(t, err)
// Verify proof.
prt := DefaultProofRuntime()
err := prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE"))
require.Nil(t, err)
// Verify proof.
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
}
m.List.cdc.MustUnmarshalBinary(bz, &res)
m.List.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
return
}
func (m Queue) setTop(top uint64) {
bz := m.List.cdc.MustMarshalBinary(top)
bz := m.List.cdc.MustMarshalBinaryLengthPrefixed(top)
m.List.store.Set(TopKey(), bz)
}

View File

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

View File

@ -295,13 +295,23 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
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)
if errMsg != nil {
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
}
@ -313,11 +323,14 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) {
err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path))
return
}
paths := strings.SplitN(path[1:], "/", 2)
storeName = paths[0]
if len(paths) == 2 {
subpath = "/" + paths[1]
}
return
}
@ -386,11 +399,12 @@ type commitInfo struct {
// Hash returns the simple merkle root hash of the stores sorted by name.
func (ci commitInfo) Hash() []byte {
// TODO cache to ci.hash []byte
m := make(map[string]merkle.Hasher, len(ci.StoreInfos))
// TODO: cache to ci.hash []byte
m := make(map[string][]byte, len(ci.StoreInfos))
for _, storeInfo := range ci.StoreInfos {
m[storeInfo.Name] = storeInfo
m[storeInfo.Name] = storeInfo.Hash()
}
return merkle.SimpleHashFromMap(m)
}
@ -422,13 +436,15 @@ type storeCore struct {
func (si storeInfo) Hash() []byte {
// Doesn't write Name, since merkle.SimpleHashFromMap() will
// include them via the keys.
bz, _ := cdc.MarshalBinary(si.Core) // Does not error
bz, _ := cdc.MarshalBinaryLengthPrefixed(si.Core)
hasher := tmhash.New()
_, err := hasher.Write(bz)
if err != nil {
// TODO: Handle with #870
panic(err)
}
return hasher.Sum(nil)
}
@ -441,16 +457,18 @@ func getLatestVersion(db dbm.DB) int64 {
if latestBytes == nil {
return 0
}
err := cdc.UnmarshalBinary(latestBytes, &latest)
err := cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest)
if err != nil {
panic(err)
}
return latest
}
// Set the latest version.
func setLatestVersion(batch dbm.Batch, version int64) {
latestBytes, _ := cdc.MarshalBinary(version) // Does not error
latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version)
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")
}
// Parse bytes.
var cInfo commitInfo
err := cdc.UnmarshalBinary(cInfoBytes, &cInfo)
err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo)
if err != nil {
return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err)
}
return cInfo, nil
}
// Set a commitInfo for given version.
func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) {
cInfoBytes, err := cdc.MarshalBinary(cInfo)
if err != nil {
panic(err)
}
cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo)
cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version)
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 {
m := make(map[string]merkle.Hasher, len(stores))
m := make(map[string][]byte, len(stores))
for key, store := range stores {
name := key.Name()
m[name] = storeInfo{
@ -224,7 +224,7 @@ func hashStores(stores map[StoreKey]CommitStore) []byte {
CommitID: store.LastCommitID(),
// StoreType: store.GetStoreType(),
},
}
}.Hash()
}
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.
func AccAddressFromBech32(address string) (addr AccAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixAccAddr)
bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixAccAddr)
if err != nil {
return nil, err
}
@ -124,7 +125,8 @@ func (aa AccAddress) Bytes() []byte {
// String implements the Stringer interface.
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 {
panic(err)
}
@ -169,7 +171,8 @@ func ValAddressFromHex(address string) (addr ValAddress, err error) {
// ValAddressFromBech32 creates a ValAddress from a Bech32 string.
func ValAddressFromBech32(address string) (addr ValAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixValAddr)
bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixValAddr)
if err != nil {
return nil, err
}
@ -239,7 +242,8 @@ func (va ValAddress) Bytes() []byte {
// String implements the Stringer interface.
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 {
panic(err)
}
@ -284,7 +288,8 @@ func ConsAddressFromHex(address string) (addr ConsAddress, err error) {
// ConsAddressFromBech32 creates a ConsAddress from a Bech32 string.
func ConsAddressFromBech32(address string) (addr ConsAddress, err error) {
bz, err := GetFromBech32(address, Bech32PrefixConsAddr)
bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix()
bz, err := GetFromBech32(address, bech32PrefixConsAddr)
if err != nil {
return nil, err
}
@ -359,7 +364,8 @@ func (ca ConsAddress) Bytes() []byte {
// String implements the Stringer interface.
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 {
panic(err)
}
@ -387,7 +393,8 @@ func (ca ConsAddress) Format(s fmt.State, verb rune) {
// Bech32ifyAccPub returns a Bech32 encoded string containing the
// Bech32PrefixAccPub prefix for a given account PubKey.
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.
@ -403,7 +410,8 @@ func MustBech32ifyAccPub(pub crypto.PubKey) string {
// Bech32ifyValPub returns a Bech32 encoded string containing the
// Bech32PrefixValPub prefix for a given validator operator's PubKey.
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.
@ -419,7 +427,8 @@ func MustBech32ifyValPub(pub crypto.PubKey) string {
// Bech32ifyConsPub returns a Bech32 encoded string containing the
// Bech32PrefixConsPub prefixfor a given consensus node's PubKey.
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
@ -436,7 +445,8 @@ func MustBech32ifyConsPub(pub crypto.PubKey) string {
// GetAccPubKeyBech32 creates a PubKey for an account with a given public key
// string using the Bech32 Bech32PrefixAccPub prefix.
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 {
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
// public key string using the Bech32 Bech32PrefixValPub prefix.
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 {
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
// key string using the Bech32 Bech32PrefixConsPub prefix.
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 {
return nil, err
}

View File

@ -9,6 +9,8 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519"
"strings"
"github.com/cosmos/cosmos-sdk/types"
)
@ -178,3 +180,44 @@ func TestConsAddress(t *testing.T) {
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
// a smaller value
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
@ -142,7 +142,11 @@ func (coins Coins) Plus(coinsB Coins) Coins {
coinA, coinB := coins[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1:
if coinA.IsZero() {
// ignore 0 sum coin type
} else {
sum = append(sum, coinA)
}
indexA++
case 0:
if coinA.Amount.Add(coinB.Amount).IsZero() {
@ -153,7 +157,11 @@ func (coins Coins) Plus(coinsB Coins) Coins {
indexA++
indexB++
case 1:
if coinB.IsZero() {
// ignore 0 sum coin type
} else {
sum = append(sum, coinB)
}
indexB++
}
}
@ -176,10 +184,19 @@ func (coins Coins) Minus(coinsB Coins) Coins {
return coins.Plus(coinsB.Negative())
}
// IsGTE returns True iff coins is NonNegative(), and for every
// currency in coinsB, the currency is present at an equal or greater
// amount in coinsB
func (coins Coins) IsGTE(coinsB Coins) bool {
// IsAllGT returns True iff for every denom in coins, the denom is present at a
// greater amount in coinsB.
func (coins Coins) IsAllGT(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)
if len(diff) == 0 {
return true
@ -187,14 +204,27 @@ func (coins Coins) IsGTE(coinsB Coins) bool {
return diff.IsNotNegative()
}
// IsLT returns True iff every currency in coins, the currency is
// present at a smaller amount in coins
func (coins Coins) IsLT(coinsB Coins) bool {
return !coins.IsGTE(coinsB)
// IsAllLT returns True iff for every denom in coins, the denom is present at
// a smaller amount in coinsB.
func (coins Coins) IsAllLT(coinsB Coins) bool {
diff := coinsB.Minus(coins)
if len(diff) == 0 {
return false
}
return diff.IsPositive()
}
// IsZero returns true if there are no coins
// or all coins are zero.
// IsAllLTE returns True iff for every denom in coins, the denom is present at
// 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 {
for _, coin := range coins {
if !coin.IsZero() {

View File

@ -86,7 +86,10 @@ func TestIsLTCoin(t *testing.T) {
{NewInt64Coin("A", 1), NewInt64Coin("A", 1), false},
{NewInt64Coin("A", 2), NewInt64Coin("A", 1), false},
{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 {
@ -245,9 +248,9 @@ func TestCoins(t *testing.T) {
assert.True(t, good.IsValid(), "Coins are valid")
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.True(t, good.IsGTE(empty), "Expected %v to be >= %v", good, empty)
assert.False(t, good.IsLT(empty), "Expected %v to be < %v", good, empty)
assert.True(t, empty.IsLT(good), "Expected %v to be < %v", empty, good)
assert.True(t, good.IsAllGTE(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.IsAllLT(good), "Expected %v to be < %v", empty, good)
assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg)
assert.Zero(t, len(sum), "Expected 0 coins")
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) {
one := NewInt(1)
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) {
d := mustNewDecFromStr(t, "0.333")
bz, err := cdc.MarshalBinary(d)
bz, err := cdc.MarshalBinaryLengthPrefixed(d)
require.NoError(t, err)
var d2 Dec
err = cdc.UnmarshalBinary(bz, &d2)
err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2)
require.NoError(t, err)
require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2)
}
@ -323,11 +323,11 @@ type testDEmbedStruct struct {
// TODO make work for UnmarshalJSON
func TestEmbeddedStructSerializationGocodec(t *testing.T) {
obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)}
bz, err := cdc.MarshalBinary(obj)
bz, err := cdc.MarshalBinaryLengthPrefixed(obj)
require.Nil(t, err)
var obj2 testDEmbedStruct
err = cdc.UnmarshalBinary(bz, &obj2)
err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2)
require.Nil(t, err)
require.Equal(t, obj.Field1, obj2.Field1)

View File

@ -64,7 +64,11 @@ type ValidatorSet interface {
func(index int64, validator Validator) (stop bool))
// 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))
Validator(Context, ValAddress) Validator // get a particular validator by operator address
@ -113,7 +117,7 @@ type DelegationSet interface {
type StakingHooks interface {
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
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
OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"fmt"
"io"
@ -127,7 +128,7 @@ type KVStore interface {
// Has checks if a key exists. Panics on nil key.
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)
// 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))
}
// 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
// the CacheKVStore, all previously created CacheKVStores on the
// object expire.

View File

@ -1,6 +1,7 @@
package types
import (
"encoding/binary"
"encoding/json"
"time"
@ -36,6 +37,13 @@ func MustSortJSON(toSortJSON []byte) []byte {
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
const SortableTimeFormat = "2006-01-02T15:04:05.000000000"

View File

@ -93,16 +93,16 @@ func TestBaseAccountMarshal(t *testing.T) {
cdc := codec.New()
codec.RegisterCrypto(cdc)
b, err := cdc.MarshalBinary(acc)
b, err := cdc.MarshalBinaryLengthPrefixed(acc)
require.Nil(t, err)
acc2 := BaseAccount{}
err = cdc.UnmarshalBinary(b, &acc2)
err = cdc.UnmarshalBinaryLengthPrefixed(b, &acc2)
require.Nil(t, err)
require.Equal(t, acc, acc2)
// error on bad bytes
acc2 = BaseAccount{}
err = cdc.UnmarshalBinary(b[:len(b)/2], &acc2)
err = cdc.UnmarshalBinaryLengthPrefixed(b[:len(b)/2], &acc2)
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.
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
return sdk.ErrInsufficientFee(fmt.Sprintf(
"insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees)).Result()

View File

@ -2,9 +2,9 @@ package cli
import (
"fmt"
"io/ioutil"
"github.com/pkg/errors"
"github.com/spf13/viper"
"io/ioutil"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
@ -13,13 +13,14 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-amino"
)
const (
flagAppend = "append"
flagPrintSigs = "print-sigs"
flagValidateSigs = "validate-signatures"
flagOffline = "offline"
flagSigOnly = "signature-only"
)
// 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.
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.
Thus account number or sequence number lookups will not be performed and it is
recommended to set such parameters manually.`,
@ -37,8 +45,11 @@ recommended to set such parameters manually.`,
Args: cobra.ExactArgs(1),
}
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(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit")
cmd.Flags().Bool(flagAppend, true,
"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.")
return cmd
}
@ -50,25 +61,46 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.
return
}
if viper.GetBool(flagPrintSigs) {
printSignatures(stdTx)
if viper.GetBool(flagValidateSigs) {
if !printSignatures(stdTx) {
return fmt.Errorf("signatures validation failed")
}
return nil
}
name := viper.GetString(client.FlagName)
if name == "" {
return errors.New("required flag \"name\" has not been set")
}
cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder)
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 {
return err
}
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, "", " ")
} else {
default:
json, err = cdc.MarshalJSON(newTx)
}
}
if err != nil {
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:")
for i, signer := range stdTx.GetSigners() {
signers := stdTx.GetSigners()
for i, signer := range signers {
fmt.Printf(" %v: %v\n", i, signer.String())
}
sigs := stdTx.GetSignatures()
fmt.Println("")
fmt.Println("Signatures:")
for i, sig := range stdTx.GetSignatures() {
fmt.Printf(" %v: %v\n", i, sdk.AccAddress(sig.Address()).String())
success := true
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) {

View File

@ -125,7 +125,8 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err
if err != nil {
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
@ -166,7 +167,7 @@ func (bldr TxBuilder) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, erro
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

View File

@ -36,12 +36,12 @@ func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
}
feePool := &(sdk.Coins{})
fck.cdc.MustUnmarshalBinary(bz, feePool)
fck.cdc.MustUnmarshalBinaryLengthPrefixed(bz, feePool)
return *feePool
}
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.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 {
accNumber = 0
} else {
err := am.cdc.UnmarshalBinary(bz, &accNumber)
err := am.cdc.UnmarshalBinaryLengthPrefixed(bz, &accNumber)
if err != nil {
panic(err)
}
}
bz = am.cdc.MustMarshalBinary(accNumber + 1)
bz = am.cdc.MustMarshalBinaryLengthPrefixed(accNumber + 1)
store.Set(globalAccountNumberKey, bz)
return accNumber

View File

@ -3,15 +3,13 @@ package auth
import (
"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"
"github.com/cosmos/cosmos-sdk/store"
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) {
@ -115,9 +113,92 @@ func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
acc := mapper.NewAccountWithAddress(ctx, addr)
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 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
// are registered by MakeTxCodec
err := cdc.UnmarshalBinary(txBytes, &tx)
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
}
return tx, nil
}
}

View File

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

View File

@ -59,7 +59,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command {
}
// 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)
}

View File

@ -22,11 +22,12 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht
return
}
txBytes, err := cliCtx.Codec.MarshalBinary(m.Tx)
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
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.
func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) bool {
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.

View File

@ -14,7 +14,7 @@ import (
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
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{})
accts := mock.GetAllAccounts(mapper, ctx)
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
// is what is expected
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{})
totalCoins := sdk.Coins{}

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