diff --git a/CHANGELOG.md b/CHANGELOG.md index f635b7ea6f..425c2cf144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ FEATURES * [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. + * [cli] [\#2704](https://github.com/cosmos/cosmos-sdk/pull/2704) New add-genesis-account convenience command to populate genesis.json with genesis accounts. * 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. @@ -47,8 +48,7 @@ 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 + * [\#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 diff --git a/Gopkg.lock b/Gopkg.lock index 8dd4be7083..3aecfa090b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -298,7 +298,7 @@ "model", ] pruneopts = "UT" - revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" + revision = "0b1957f9d949dfa3084171a6ec5642b38055276a" [[projects]] branch = "master" @@ -434,7 +434,7 @@ version = "v0.11.1" [[projects]] - digest = "1:395820b381043b9d2204e181ddf0f9147397c4a7b8f5dc3162de4cfcddf4589a" + digest = "1:ba2ba7d6a0853472bdb7a64c4f9c1d5f9cba0eb7aac0b024654104387bf5eb57" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -500,8 +500,8 @@ "version", ] pruneopts = "UT" - revision = "03e42d2e3866f01a00625f608e3bbfaeb30690de" - version = "v0.26.1-rc0" + revision = "80d0a362500fea2dd089258319075a54e5d40a2d" + version = "v0.26.1" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index 5726008858..90f54c6570 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -36,7 +36,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "v0.26.1-rc0" # TODO replace w/ 0.26.1 + version = "v0.26.1" ## deps without releases: diff --git a/Makefile b/Makefile index aaa2fbcf3e..3d76c78f7d 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,9 @@ PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') -VERSION := $(shell git describe --tags --long | sed 's/v\(.*\)/\1/') -BUILD_TAGS = netgo ledger +VERSION := $(subst v,,$(shell git describe --tags --long)) +BUILD_TAGS = netgo BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.Version=${VERSION}" -GCC := $(shell command -v gcc 2> /dev/null) LEDGER_ENABLED ?= true -UNAME_S := $(shell uname -s) GOTOOLS = \ github.com/golang/dep/cmd/dep \ github.com/alecthomas/gometalinter \ @@ -20,23 +18,30 @@ ci: get_tools get_vendor_deps install test_cover test_lint test ######################################## ### Build/Install -check-ledger: ifeq ($(LEDGER_ENABLED),true) - ifeq ($(UNAME_S),OpenBSD) - $(info "OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)") -TMP_BUILD_TAGS := $(BUILD_TAGS) -BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) - else - ifndef GCC - $(error "gcc not installed for ledger support, please install or set LEDGER_ENABLED to false in the Makefile") - endif - endif -else -TMP_BUILD_TAGS := $(BUILD_TAGS) -BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) + ifeq ($(OS),Windows_NT) + GCCEXE = $(shell where gcc.exe 2> NUL) + ifeq ($(GCCEXE),) + $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + BUILD_TAGS += ledger + endif + else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S),OpenBSD) + $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) + else + GCC = $(shell command -v gcc 2> /dev/null) + ifeq ($(GCC),) + $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + BUILD_TAGS += ledger + endif + endif + endif endif -build: check-ledger update_gaia_lite_docs +build: ifeq ($(OS),Windows_NT) go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli @@ -101,7 +106,7 @@ check_tools: update_tools: @echo "--> Updating tools to correct version" - ./scripts/get_tools.sh + $(MAKE) -C scripts get_tools update_dev_tools: @echo "--> Downloading linters (this may take awhile)" @@ -110,7 +115,7 @@ update_dev_tools: get_tools: @echo "--> Installing tools" - ./scripts/get_tools.sh + $(MAKE) -C scripts get_tools get_dev_tools: @echo "--> Downloading linters (this may take awhile)" diff --git a/PENDING.md b/PENDING.md index dda0c70808..35403a5ce1 100644 --- a/PENDING.md +++ b/PENDING.md @@ -5,10 +5,13 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + * [cli] [\#2727](https://github.com/cosmos/cosmos-sdk/pull/2727) Fix unbonding command flow + * [cli] [\#2786](https://github.com/cosmos/cosmos-sdk/pull/2786) Fix redelegation command flow * Gaia * SDK + * [\#2752](https://github.com/cosmos/cosmos-sdk/pull/2752) Don't hardcode bondable denom. * Tendermint @@ -23,6 +26,7 @@ FEATURES * Gaia * SDK + * [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time * Tendermint @@ -32,10 +36,13 @@ IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet * Gaia + - #2672 [Makefile] Updated for better Windows compatibility and ledger support logic, get_tools was rewritten as a cross-compatible Makefile. * SDK + - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization * Tendermint @@ -47,7 +54,10 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia + * [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address` + * [\#2742](https://github.com/cosmos/cosmos-sdk/issues/2742) Fix time format of TimeoutCommit override * SDK * Tendermint + * [\#2797](https://github.com/tendermint/tendermint/pull/2797) AddressBook requires addresses to have IDs; Do not crap out immediately after sending pex addrs in seed mode diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 3dfb29e519..1f087ec88e 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -31,6 +31,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) func init() { @@ -265,7 +266,7 @@ func TestCoinSend(t *testing.T) { coins := acc.GetCoins() mycoins := coins[0] - require.Equal(t, "steak", mycoins.Denom) + require.Equal(t, stakeTypes.DefaultBondDenom, mycoins.Denom) require.Equal(t, initialBalance[0].Amount.SubRaw(1), mycoins.Amount) // query receiver @@ -273,7 +274,7 @@ func TestCoinSend(t *testing.T) { coins = acc.GetCoins() mycoins = coins[0] - require.Equal(t, "steak", mycoins.Denom) + require.Equal(t, stakeTypes.DefaultBondDenom, mycoins.Denom) require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas @@ -326,7 +327,7 @@ func DisabledTestIBCTransfer(t *testing.T) { coins := acc.GetCoins() mycoins := coins[0] - require.Equal(t, "steak", mycoins.Denom) + require.Equal(t, stakeTypes.DefaultBondDenom, mycoins.Denom) require.Equal(t, initialBalance[0].Amount.SubRaw(1), mycoins.Amount) // TODO: query ibc egress packet state @@ -514,7 +515,7 @@ func TestValidatorQuery(t *testing.T) { } func TestBonding(t *testing.T) { - name, password, denom := "test", "1234567890", "steak" + name, password, denom := "test", "1234567890", stakeTypes.DefaultBondDenom addr, seed := CreateAddr(t, name, password, GetKeyBase(t)) cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr}) @@ -568,7 +569,7 @@ func TestBonding(t *testing.T) { // sender should have not received any coins as the unbonding has only just begun acc = getAccount(t, port, addr) coins = acc.GetCoins() - require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) + require.Equal(t, int64(40), coins.AmountOf(stakeTypes.DefaultBondDenom).Int64()) unbonding := getUndelegation(t, port, addr, operAddrs[0]) require.Equal(t, "30", unbonding.Balance.Amount.String()) @@ -667,11 +668,11 @@ func TestDeposit(t *testing.T) { // query proposal proposal = getProposal(t, port, proposalID) - require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewInt64Coin("steak", 10)})) + require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)})) // query deposit deposit := getDeposit(t, port, proposalID, addr) - require.True(t, deposit.Amount.IsEqual(sdk.Coins{sdk.NewInt64Coin("steak", 10)})) + require.True(t, deposit.Amount.IsEqual(sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)})) } func TestVote(t *testing.T) { @@ -865,7 +866,7 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) // send - coinbz, err := cdc.MarshalJSON(sdk.NewInt64Coin("steak", 1)) + coinbz, err := cdc.MarshalJSON(sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1)) if err != nil { panic(err) } @@ -951,7 +952,7 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc "account_number":"%d", "sequence":"%d" } - }`, "steak", name, password, chainID, accnum, sequence)) + }`, stakeTypes.DefaultBondDenom, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/ibc/testchain/%s/send", receiveAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -1100,7 +1101,7 @@ func doDelegate(t *testing.T, port, seed, name, password string, "account_number":"%d", "sequence":"%d" } - }`, delAddr, valAddr, "steak", amount, name, password, chainID, accnum, sequence)) + }`, delAddr, valAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -1354,7 +1355,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA "description": "test", "proposal_type": "Text", "proposer": "%s", - "initial_deposit": [{ "denom": "steak", "amount": "%d" }], + "initial_deposit": [{ "denom": "%s", "amount": "%d" }], "base_req": { "name": "%s", "password": "%s", @@ -1362,7 +1363,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA "account_number":"%d", "sequence":"%d" } - }`, proposerAddr, amount, name, password, chainID, accnum, sequence)) + }`, proposerAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", "/gov/proposals", jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -1384,7 +1385,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk // deposit on proposal jsonStr := []byte(fmt.Sprintf(`{ "depositer": "%s", - "amount": [{ "denom": "steak", "amount": "%d" }], + "amount": [{ "denom": "%s", "amount": "%d" }], "base_req": { "name": "%s", "password": "%s", @@ -1392,7 +1393,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk "account_number":"%d", "sequence": "%d" } - }`, proposerAddr, amount, name, password, chainID, accnum, sequence)) + }`, proposerAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 39c76865cc..283550a16a 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/tendermint/tendermint/crypto/secp256k1" "io/ioutil" "net" @@ -227,7 +228,7 @@ func InitializeTestLCD( msg := stake.NewMsgCreateValidator( sdk.ValAddress(operAddr), pubKey, - sdk.NewCoin("steak", sdk.NewInt(int64(delegation))), + sdk.NewCoin(stakeTypes.DefaultBondDenom, sdk.NewInt(int64(delegation))), stake.Description{Moniker: fmt.Sprintf("validator-%d", i+1)}, stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), ) @@ -245,7 +246,7 @@ func InitializeTestLCD( valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(operAddr)) - accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 150)} + accAuth.Coins = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 150)} accs = append(accs, gapp.NewGenesisAccount(&accAuth)) } @@ -259,7 +260,7 @@ func InitializeTestLCD( // add some tokens to init accounts for _, addr := range initAddrs { accAuth := auth.NewBaseAccountWithAddress(addr) - accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 100)} + accAuth.Coins = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 100)} acc := gapp.NewGenesisAccount(&accAuth) genesisState.Accounts = append(genesisState.Accounts, acc) genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewDec(100)) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 38256e4150..e3c869ada1 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" tmtypes "github.com/tendermint/tendermint/types" ) @@ -26,7 +27,7 @@ var ( // bonded tokens given to genesis validators/accounts freeFermionVal = int64(100) freeFermionsAcc = sdk.NewInt(150) - bondDenom = "steak" + bondDenom = stakeTypes.DefaultBondDenom ) // State to Unmarshal @@ -286,9 +287,11 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { accAuth := auth.NewBaseAccountWithAddress(addr) - accAuth.Coins = []sdk.Coin{ + coins :=sdk.Coins{ {"fooToken", sdk.NewInt(1000)}, - {"steak", freeFermionsAcc}, + {bondDenom, freeFermionsAcc}, } + coins.Sort() + accAuth.Coins = coins return NewGenesisAccount(&accAuth) } diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 579ad93a85..1b1c196469 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -92,10 +92,8 @@ 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(bondDenom, 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, "") } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 60befcc385..e56344554a 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -29,6 +29,7 @@ import ( slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" stake "github.com/cosmos/cosmos-sdk/x/stake" stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( @@ -62,7 +63,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { // Randomly generate some genesis accounts for _, acc := range accs { - coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amount)}} + coins := sdk.Coins{sdk.Coin{stakeTypes.DefaultBondDenom, sdk.NewInt(amount)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ Address: acc.Address, Coins: coins, @@ -73,7 +74,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { govGenesis := gov.GenesisState{ StartingProposalID: uint64(r.Intn(100)), DepositParams: gov.DepositParams{ - MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))}, + MinDeposit: sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, int64(r.Intn(1e3)))}, MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second, }, VotingParams: gov.VotingParams{ @@ -91,7 +92,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { Params: stake.Params{ UnbondingTime: time.Duration(r.Intn(60*60*24*3*2)) * time.Second, MaxValidators: uint16(r.Intn(250)), - BondDenom: "steak", + BondDenom: stakeTypes.DefaultBondDenom, }, } fmt.Printf("Selected randomly generated staking parameters: %+v\n", stakeGenesis) @@ -113,7 +114,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), }, Params: mint.Params{ - MintDenom: "steak", + MintDenom: stakeTypes.DefaultBondDenom, InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), InflationMax: sdk.NewDecWithPrec(20, 2), InflationMin: sdk.NewDecWithPrec(7, 2), diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 0e63e4e115..5a198e9fc8 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -29,6 +29,7 @@ import ( "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" ) var ( @@ -55,10 +56,10 @@ func TestGaiaCLIMinimumFees(t *testing.T) { barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) success := executeWrite(t, fmt.Sprintf( - "gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + "gaiacli tx send %v --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.False(t, success) tests.WaitForNextNBlocksTM(2, port) @@ -121,40 +122,40 @@ func TestGaiaCLISend(t *testing.T) { barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) - executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) // Test --dry-run - success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo --dry-run", flags, barAddr), app.DefaultKeyPass) + success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10%s --to=%s --from=foo --dry-run", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.True(t, success) // Check state didn't change fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) // test autosequencing - executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(20), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(30), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) // test memo - executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10%s --to=%s --from=foo --memo 'testmemo'", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(30), barAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(30), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(20), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(20), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) } func TestGaiaCLIGasAuto(t *testing.T) { @@ -172,26 +173,26 @@ func TestGaiaCLIGasAuto(t *testing.T) { barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) // Test failure with auto gas disabled and very little gas set by hand - success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=10 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=10 --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.False(t, success) tests.WaitForNextNBlocksTM(2, port) // Check state didn't change fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) // Test failure with negative gas - success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=-100 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=-100 --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.False(t, success) // Test failure with 0 gas - success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=0 --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.False(t, success) // Enable auto gas - success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx send %v --json --gas=simulate --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx send %v --json --gas=simulate --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) require.True(t, success) // check that gas wanted == gas used cdc := app.MakeCodec() @@ -205,7 +206,7 @@ func TestGaiaCLIGasAuto(t *testing.T) { tests.WaitForNextNBlocksTM(2, port) // Check state has changed accordingly fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) } func TestGaiaCLICreateValidator(t *testing.T) { @@ -223,13 +224,13 @@ func TestGaiaCLICreateValidator(t *testing.T) { barAddr, barPubKey := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) barCeshPubKey := sdk.MustBech32ifyConsPub(barPubKey) - executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10%s --to=%s --from=foo", flags, stakeTypes.DefaultBondDenom, barAddr), app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) defaultParams := stake.DefaultParams() initialPool := stake.InitialPool() @@ -239,7 +240,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { cvStr := fmt.Sprintf("gaiacli tx create-validator %v", flags) cvStr += fmt.Sprintf(" --from=%s", "bar") cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey) - cvStr += fmt.Sprintf(" --amount=%v", "2steak") + cvStr += fmt.Sprintf(" --amount=%v", fmt.Sprintf("2%s", stakeTypes.DefaultBondDenom)) cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally") cvStr += fmt.Sprintf(" --commission-rate=%v", "0.05") cvStr += fmt.Sprintf(" --commission-max-rate=%v", "0.20") @@ -265,7 +266,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { tests.WaitForNextNBlocksTM(2, port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc) + require.Equal(t, int64(8), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64(), "%v", barAcc) validator := executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, validator.OperatorAddr, sdk.ValAddress(barAddr)) @@ -287,7 +288,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { /* // this won't be what we expect because we've only started unbonding, haven't completed barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %v %v", barCech, flags)) - require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc) + require.Equal(t, int64(9), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64(), "%v", barAcc) */ validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, "1.0000000000", validator.Tokens.String()) @@ -319,7 +320,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) proposalsQuery, _ := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") require.Equal(t, "No matching proposals found", proposalsQuery) @@ -327,7 +328,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // submit a test proposal spStr := fmt.Sprintf("gaiacli tx submit-proposal %v", flags) spStr += fmt.Sprintf(" --from=%s", "foo") - spStr += fmt.Sprintf(" --deposit=%s", "5steak") + spStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("5%s", stakeTypes.DefaultBondDenom)) spStr += fmt.Sprintf(" --type=%s", "Text") spStr += fmt.Sprintf(" --title=%s", "Test") spStr += fmt.Sprintf(" --description=%s", "test") @@ -350,7 +351,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { tests.WaitForNextNBlocksTM(2, port) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) require.Equal(t, uint64(1), proposal1.GetProposalID()) @@ -362,11 +363,11 @@ func TestGaiaCLISubmitProposal(t *testing.T) { deposit := executeGetDeposit(t, fmt.Sprintf("gaiacli query deposit --proposal-id=1 --depositer=%s --output=json %v", fooAddr, flags)) - require.Equal(t, int64(5), deposit.Amount.AmountOf("steak").Int64()) + require.Equal(t, int64(5), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) depositStr := fmt.Sprintf("gaiacli tx deposit %v", flags) depositStr += fmt.Sprintf(" --from=%s", "foo") - depositStr += fmt.Sprintf(" --deposit=%s", "10steak") + depositStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("10%s", stakeTypes.DefaultBondDenom)) depositStr += fmt.Sprintf(" --proposal-id=%s", "1") // Test generate only @@ -386,15 +387,15 @@ func TestGaiaCLISubmitProposal(t *testing.T) { deposits := executeGetDeposits(t, fmt.Sprintf("gaiacli query deposits --proposal-id=1 --output=json %v", flags)) require.Len(t, deposits, 1) - require.Equal(t, int64(15), deposits[0].Amount.AmountOf("steak").Int64()) + require.Equal(t, int64(15), deposits[0].Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) deposit = executeGetDeposit(t, fmt.Sprintf("gaiacli query deposit --proposal-id=1 --depositer=%s --output=json %v", fooAddr, flags)) - require.Equal(t, int64(15), deposit.Amount.AmountOf("steak").Int64()) + require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) require.Equal(t, uint64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus()) @@ -435,7 +436,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // submit a second test proposal spStr = fmt.Sprintf("gaiacli tx submit-proposal %v", flags) spStr += fmt.Sprintf(" --from=%s", "foo") - spStr += fmt.Sprintf(" --deposit=%s", "5steak") + spStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("5%s", stakeTypes.DefaultBondDenom)) spStr += fmt.Sprintf(" --type=%s", "Text") spStr += fmt.Sprintf(" --title=%s", "Apples") spStr += fmt.Sprintf(" --description=%s", "test") @@ -464,8 +465,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test generate sendTx with default gas success, stdout, stderr := executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli tx send %v --amount=10steak --to=%s --from=foo --generate-only", - flags, barAddr), []string{}...) + "gaiacli tx send %v --amount=10%s --to=%s --from=foo --generate-only", + flags, stakeTypes.DefaultBondDenom, barAddr), []string{}...) require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) @@ -475,8 +476,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test generate sendTx with --gas=$amount success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli tx send %v --amount=10steak --to=%s --from=foo --gas=100 --generate-only", - flags, barAddr), []string{}...) + "gaiacli tx send %v --amount=10%s --to=%s --from=foo --gas=100 --generate-only", + flags, stakeTypes.DefaultBondDenom, barAddr), []string{}...) require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -486,8 +487,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test generate sendTx, estimate gas success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli tx send %v --amount=10steak --to=%s --from=foo --gas=simulate --generate-only", - flags, barAddr), []string{}...) + "gaiacli tx send %v --amount=10%s --to=%s --from=foo --gas=simulate --generate-only", + flags, stakeTypes.DefaultBondDenom, barAddr), []string{}...) require.True(t, success) require.NotEmpty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -526,7 +527,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test broadcast fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( "gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) @@ -540,9 +541,9 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { tests.WaitForNextNBlocksTM(2, port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) } func TestGaiaCLIConfig(t *testing.T) { diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index ae076571b5..ef7b39d111 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -42,6 +42,7 @@ func main() { rootCmd.AddCommand(gaiaInit.CollectGenTxsCmd(ctx, cdc)) rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, server.AppInit{})) rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) + rootCmd.AddCommand(gaiaInit.AddGenesisAccountCmd(ctx, cdc)) server.AddCommands(ctx, cdc, rootCmd, appInit, newApp, exportAppStateAndTMValidators) diff --git a/cmd/gaia/init/genesis_accts.go b/cmd/gaia/init/genesis_accts.go new file mode 100644 index 0000000000..3d43712fcc --- /dev/null +++ b/cmd/gaia/init/genesis_accts.go @@ -0,0 +1,66 @@ +package init + +import ( + "fmt" + + "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/cosmos/cosmos-sdk/x/auth" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/common" +) + +// AddGenesisAccountCmd returns add-genesis-account cobra Command +func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-genesis-account [address] [coin][,[coin]]", + Short: "Add genesis account to genesis.json", + Args: cobra.ExactArgs(2), + RunE: func(_ *cobra.Command, args []string) error { + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + coins, err := sdk.ParseCoins(args[1]) + if err != nil { + return err + } + coins.Sort() + + genFile := config.GenesisFile() + if !common.FileExists(genFile) { + return fmt.Errorf("%s does not exist, run `gaiad init` first", genFile) + } + genDoc, err := loadGenesisDoc(cdc, genFile) + if err != nil { + return err + } + + var appState app.GenesisState + if err = cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { + return err + } + + acc := auth.NewBaseAccountWithAddress(addr) + acc.Coins = coins + appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc)) + + appStateJSON, err := cdc.MarshalJSON(appState) + if err != nil { + return err + } + + return ExportGenesisFile(genFile, genDoc.ChainID, nil, appStateJSON) + }, + } + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + return cmd +} diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 7ed8203754..449eb2b85b 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" @@ -21,7 +22,7 @@ import ( ) const ( - defaultAmount = "100steak" + defaultAmount = "100" + stakeTypes.DefaultBondDenom defaultCommissionRate = "0.1" defaultCommissionMaxRate = "0.2" defaultCommissionMaxChangeRate = "0.01" diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 8bada583e2..b0676515c1 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -14,6 +14,7 @@ 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" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" @@ -75,14 +76,20 @@ Example: 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, ...)") + cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + return cmd } func initTestnet(config *cfg.Config, cdc *codec.Codec) error { + var chainID string outDir := viper.GetString(flagOutputDir) numValidators := viper.GetInt(flagNumValidators) - chainID := "chain-" + cmn.RandStr(6) + chainID = viper.GetString(client.FlagChainID) + if chainID == "" { + chainID = "chain-" + cmn.RandStr(6) + } monikers := make([]string, numValidators) nodeIDs := make([]string, numValidators) @@ -174,14 +181,14 @@ func initTestnet(config *cfg.Config, cdc *codec.Codec) error { Address: addr, Coins: sdk.Coins{ sdk.NewInt64Coin(fmt.Sprintf("%sToken", nodeDirName), 1000), - sdk.NewInt64Coin("steak", 150), + sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 150), }, }) msg := stake.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], - sdk.NewInt64Coin("steak", 100), + sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 100), stake.NewDescription(nodeDirName, "", "", ""), stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), ) diff --git a/examples/democoin/x/simplestake/keeper.go b/examples/democoin/x/simplestake/keeper.go index 607f61d83c..b757dd66de 100644 --- a/examples/democoin/x/simplestake/keeper.go +++ b/examples/democoin/x/simplestake/keeper.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" ) -const stakingToken = "steak" +const stakingToken = "stake" const moduleName = "simplestake" diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index c3876cf4ad..974cf50e9e 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -75,10 +75,10 @@ func TestBonding(t *testing.T) { _, _, err := stakeKeeper.unbondWithoutCoins(ctx, addr) require.Equal(t, err, ErrInvalidUnbond(DefaultCodespace)) - _, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin("steak", 10)) + _, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin("stake", 10)) require.Nil(t, err) - power, err := stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin("steak", 10)) + power, err := stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin("stake", 10)) require.Nil(t, err) require.Equal(t, int64(20), power) diff --git a/scripts/Makefile b/scripts/Makefile new file mode 100644 index 0000000000..9abc497e45 --- /dev/null +++ b/scripts/Makefile @@ -0,0 +1,54 @@ +### +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +### +ifeq ($(OS),Windows_NT) + GO := $(shell where go.exe 2> NUL) + FS := \\ +else + GO := $(shell command -v go 2> /dev/null) + FS := / +endif + +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif + +GOPATH ?= $(shell $(GO) env GOPATH) +GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com + +### +# Functions +### + +go_get = $(if $(findstring Windows_NT,$(OS)),\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\ +,\ +mkdir -p $(GITHUBDIR)$(FS)$(1) &&\ +(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\ +)\ +cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) + +go_install = $(call go_get,$(1),$(2),$(3)) && cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && $(GO) install + +### +# get_tools +### +all: get_tools +get_tools: dep gometalinter statik + +dep: + $(call go_get,golang,dep,22125cfaa6ddc71e145b1535d4b7ee9744fefff2) + cd $(GITHUBDIR)$(FS)golang$(FS)dep$(FS)cmd$(FS)dep && $(GO) install + +#v2.0.11 +gometalinter: + $(call go_install,alecthomas,gometalinter,17a7ffa42374937bfecabfb8d2efbd4db0c26741) + +statik: + $(call go_install,rakyll,statik,v0.1.5) + +.PHONY: all get_tools dep gometalinter statik + diff --git a/scripts/get_tools.sh b/scripts/get_tools.sh deleted file mode 100755 index 79ab32deca..0000000000 --- a/scripts/get_tools.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This file downloads all of the binary dependencies we have, and checks out a -# specific git hash. -# -# repos it installs: -# github.com/golang/dep/cmd/dep -# gopkg.in/alecthomas/gometalinter.v2 -# github.com/rakyll/statiik - -## check if GOPATH is set -if [ -z ${GOPATH+x} ]; then - echo "please set GOPATH (https://github.com/golang/go/wiki/SettingGOPATH)" - exit 1 -fi - -mkdir -p "$GOPATH/src/github.com" -cd "$GOPATH/src/github.com" || exit 1 - -installFromGithub() { - repo=$1 - commit=$2 - # optional - subdir=$3 - echo "--> Installing $repo ($commit)..." - if [ ! -d "$repo" ]; then - mkdir -p "$repo" - git clone "https://github.com/$repo.git" "$repo" - fi - if [ ! -z ${subdir+x} ] && [ ! -d "$repo/$subdir" ]; then - echo "ERROR: no such directory $repo/$subdir" - exit 1 - fi - pushd "$repo" && \ - git fetch origin && \ - git checkout -q "$commit" && \ - if [ ! -z ${subdir+x} ]; then cd "$subdir" || exit 1; fi && \ - go install && \ - if [ ! -z ${subdir+x} ]; then cd - || exit 1; fi && \ - popd || exit 1 - echo "--> Done" - echo "" -} - -installFromGithub golang/dep 22125cfaa6ddc71e145b1535d4b7ee9744fefff2 cmd/dep -## gometalinter v2.0.11 -installFromGithub alecthomas/gometalinter 17a7ffa42374937bfecabfb8d2efbd4db0c26741 -installFromGithub rakyll/statik v0.1.5 \ No newline at end of file diff --git a/scripts/multisim.sh b/scripts/multisim.sh index ff5784e4bf..2376eb3a8f 100755 --- a/scripts/multisim.sh +++ b/scripts/multisim.sh @@ -17,7 +17,7 @@ echo "Using temporary log directory: $tmpdir" sim() { seed=$1 echo "Running full Gaia simulation with seed $seed. This may take awhile!" - file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout" + file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout" echo "Writing stdout to $file..." go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=$blocks \ -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 82652bdec5..5aeacf92f3 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -63,17 +63,17 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { func ShowAddressCmd(ctx *Context) *cobra.Command { cmd := &cobra.Command{ Use: "show-address", - Short: "Shows this node's tendermint validator address", + Short: "Shows this node's tendermint validator consensus address", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()) - valAddr := (sdk.ValAddress)(privValidator.Address) + valConsAddr := (sdk.ConsAddress)(privValidator.Address) if viper.GetBool(client.FlagJson) { - return printlnJSON(valAddr) + return printlnJSON(valConsAddr) } - fmt.Println(valAddr.String()) + fmt.Println(valConsAddr.String()) return nil }, } diff --git a/server/util.go b/server/util.go index 51f10c765a..633ad88704 100644 --- a/server/util.go +++ b/server/util.go @@ -7,6 +7,7 @@ import ( "os/signal" "path/filepath" "syscall" + "time" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -92,7 +93,7 @@ func interceptLoadConfig() (conf *cfg.Config, err error) { conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 conf.TxIndex.IndexAllTags = true - conf.Consensus.TimeoutCommit = 5000 + conf.Consensus.TimeoutCommit = 5 * time.Second cfg.WriteConfigFile(configFilePath, conf) // Fall through, just so that its parsed into memory. } diff --git a/types/coin_test.go b/types/coin_test.go index ac9fcb391b..77307f22a5 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -49,8 +49,8 @@ func TestSameDenomAsCoin(t *testing.T) { {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, - {NewInt64Coin("steak", 1), NewInt64Coin("steak", 10), true}, - {NewInt64Coin("steak", -11), NewInt64Coin("steak", 10), true}, + {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), true}, + {NewInt64Coin("stake", -11), NewInt64Coin("stake", 10), true}, } for tcIndex, tc := range cases { @@ -107,8 +107,8 @@ func TestIsEqualCoin(t *testing.T) { {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, - {NewInt64Coin("steak", 1), NewInt64Coin("steak", 10), false}, - {NewInt64Coin("steak", -11), NewInt64Coin("steak", 10), false}, + {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), false}, + {NewInt64Coin("stake", -11), NewInt64Coin("stake", 10), false}, } for tcIndex, tc := range cases { diff --git a/types/utils_test.go b/types/utils_test.go index dbdd08c0ab..f3930d1521 100644 --- a/types/utils_test.go +++ b/types/utils_test.go @@ -21,8 +21,8 @@ func TestSortJSON(t *testing.T) { want: "", wantErr: true}, // genesis.json - {unsortedJSON: `{"consensus_params":{"block_size_params":{"max_bytes":22020096,"max_txs":100000,"max_gas":-1},"tx_size_params":{"max_bytes":10240,"max_gas":-1},"block_gossip_params":{"block_part_size_bytes":65536},"evidence_params":{"max_age":100000}},"validators":[{"pub_key":{"type":"AC26791624DE60","value":"c7UMMAbjFuc5GhGPy0E5q5tefy12p9Tq0imXqdrKXwo="},"power":100,"name":""}],"app_hash":"","genesis_time":"2018-05-11T15:52:25.424795506Z","chain_id":"test-chain-Q6VeoW","app_state":{"accounts":[{"address":"718C9C23F98C9642569742ADDD9F9AB9743FBD5D","coins":[{"denom":"Token","amount":1000},{"denom":"steak","amount":50}]}],"stake":{"pool":{"total_supply":50,"bonded_shares":"0","unbonded_shares":"0","bonded_pool":0,"unbonded_pool":0,"inflation_last_time":0,"inflation":"7/100"},"params":{"inflation_rate_change":"13/100","inflation_max":"1/5","inflation_min":"7/100","goal_bonded":"67/100","max_validators":100,"bond_denom":"steak"},"candidates":null,"bonds":null}}}`, - want: `{"app_hash":"","app_state":{"accounts":[{"address":"718C9C23F98C9642569742ADDD9F9AB9743FBD5D","coins":[{"amount":1000,"denom":"Token"},{"amount":50,"denom":"steak"}]}],"stake":{"bonds":null,"candidates":null,"params":{"bond_denom":"steak","goal_bonded":"67/100","inflation_max":"1/5","inflation_min":"7/100","inflation_rate_change":"13/100","max_validators":100},"pool":{"bonded_pool":0,"bonded_shares":"0","inflation":"7/100","inflation_last_time":0,"total_supply":50,"unbonded_pool":0,"unbonded_shares":"0"}}},"chain_id":"test-chain-Q6VeoW","consensus_params":{"block_gossip_params":{"block_part_size_bytes":65536},"block_size_params":{"max_bytes":22020096,"max_gas":-1,"max_txs":100000},"evidence_params":{"max_age":100000},"tx_size_params":{"max_bytes":10240,"max_gas":-1}},"genesis_time":"2018-05-11T15:52:25.424795506Z","validators":[{"name":"","power":100,"pub_key":{"type":"AC26791624DE60","value":"c7UMMAbjFuc5GhGPy0E5q5tefy12p9Tq0imXqdrKXwo="}}]}`, + {unsortedJSON: `{"consensus_params":{"block_size_params":{"max_bytes":22020096,"max_txs":100000,"max_gas":-1},"tx_size_params":{"max_bytes":10240,"max_gas":-1},"block_gossip_params":{"block_part_size_bytes":65536},"evidence_params":{"max_age":100000}},"validators":[{"pub_key":{"type":"AC26791624DE60","value":"c7UMMAbjFuc5GhGPy0E5q5tefy12p9Tq0imXqdrKXwo="},"power":100,"name":""}],"app_hash":"","genesis_time":"2018-05-11T15:52:25.424795506Z","chain_id":"test-chain-Q6VeoW","app_state":{"accounts":[{"address":"718C9C23F98C9642569742ADDD9F9AB9743FBD5D","coins":[{"denom":"Token","amount":1000},{"denom":"stake","amount":50}]}],"stake":{"pool":{"total_supply":50,"bonded_shares":"0","unbonded_shares":"0","bonded_pool":0,"unbonded_pool":0,"inflation_last_time":0,"inflation":"7/100"},"params":{"inflation_rate_change":"13/100","inflation_max":"1/5","inflation_min":"7/100","goal_bonded":"67/100","max_validators":100,"bond_denom":"stake"},"candidates":null,"bonds":null}}}`, + want: `{"app_hash":"","app_state":{"accounts":[{"address":"718C9C23F98C9642569742ADDD9F9AB9743FBD5D","coins":[{"amount":1000,"denom":"Token"},{"amount":50,"denom":"stake"}]}],"stake":{"bonds":null,"candidates":null,"params":{"bond_denom":"stake","goal_bonded":"67/100","inflation_max":"1/5","inflation_min":"7/100","inflation_rate_change":"13/100","max_validators":100},"pool":{"bonded_pool":0,"bonded_shares":"0","inflation":"7/100","inflation_last_time":0,"total_supply":50,"unbonded_pool":0,"unbonded_shares":"0"}}},"chain_id":"test-chain-Q6VeoW","consensus_params":{"block_gossip_params":{"block_part_size_bytes":65536},"block_size_params":{"max_bytes":22020096,"max_gas":-1,"max_txs":100000},"evidence_params":{"max_age":100000},"tx_size_params":{"max_bytes":10240,"max_gas":-1}},"genesis_time":"2018-05-11T15:52:25.424795506Z","validators":[{"name":"","power":100,"pub_key":{"type":"AC26791624DE60","value":"c7UMMAbjFuc5GhGPy0E5q5tefy12p9Tq0imXqdrKXwo="}}]}`, wantErr: false}, // from the TXSpec: {unsortedJSON: `{"chain_id":"test-chain-1","sequence":1,"fee_bytes":{"amount":[{"amount":5,"denom":"photon"}],"gas":10000},"msg_bytes":{"inputs":[{"address":"696E707574","coins":[{"amount":10,"denom":"atom"}]}],"outputs":[{"address":"6F7574707574","coins":[{"amount":10,"denom":"atom"}]}]},"alt_bytes":null}`, diff --git a/x/auth/client/txbuilder/txbuilder_test.go b/x/auth/client/txbuilder/txbuilder_test.go index 996d9b8b1b..3ad2ad4123 100644 --- a/x/auth/client/txbuilder/txbuilder_test.go +++ b/x/auth/client/txbuilder/txbuilder_test.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/tendermint/tendermint/crypto/ed25519" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( @@ -46,7 +47,7 @@ func TestTxBuilderBuild(t *testing.T) { SimulateGas: false, ChainID: "test-chain", Memo: "hello", - Fee: "1steak", + Fee: "1" + stakeTypes.DefaultBondDenom, }, defaultMsg, StdSignMsg{ @@ -55,7 +56,7 @@ func TestTxBuilderBuild(t *testing.T) { Sequence: 1, Memo: "hello", Msgs: defaultMsg, - Fee: auth.NewStdFee(100, sdk.NewCoin("steak", sdk.NewInt(1))), + Fee: auth.NewStdFee(100, sdk.NewCoin(stakeTypes.DefaultBondDenom, sdk.NewInt(1))), }, false, }, diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 67c05d3206..3a33870e14 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -15,41 +15,40 @@ const ( costAddCoins sdk.Gas = 10 ) +//----------------------------------------------------------------------------- +// Keeper + +var _ Keeper = (*BaseKeeper)(nil) + // Keeper defines a module interface that facilitates the transfer of coins // between accounts. type Keeper interface { SendKeeper + SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) } -var _ Keeper = (*BaseKeeper)(nil) - // BaseKeeper manages transfers between accounts. It implements the Keeper // interface. type BaseKeeper struct { - am auth.AccountKeeper + BaseSendKeeper + + ak auth.AccountKeeper } // NewBaseKeeper returns a new BaseKeeper -func NewBaseKeeper(am auth.AccountKeeper) BaseKeeper { - return BaseKeeper{am: am} -} - -// GetCoins returns the coins at the addr. -func (keeper BaseKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { - return getCoins(ctx, keeper.am, addr) +func NewBaseKeeper(ak auth.AccountKeeper) BaseKeeper { + return BaseKeeper{ + BaseSendKeeper: NewBaseSendKeeper(ak), + ak: ak, + } } // SetCoins sets the coins at the addr. func (keeper BaseKeeper) SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) sdk.Error { - return setCoins(ctx, keeper.am, addr, amt) -} - -// HasCoins returns whether or not an account has at least amt coins. -func (keeper BaseKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool { - return hasCoins(ctx, keeper.am, addr, amt) + return setCoins(ctx, keeper.ak, addr, amt) } // SubtractCoins subtracts amt from the coins at the addr. @@ -57,7 +56,7 @@ func (keeper BaseKeeper) SubtractCoins( ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, ) (sdk.Coins, sdk.Tags, sdk.Error) { - return subtractCoins(ctx, keeper.am, addr, amt) + return subtractCoins(ctx, keeper.ak, addr, amt) } // AddCoins adds amt to the coins at the addr. @@ -65,28 +64,17 @@ func (keeper BaseKeeper) AddCoins( ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, ) (sdk.Coins, sdk.Tags, sdk.Error) { - return addCoins(ctx, keeper.am, addr, amt) + return addCoins(ctx, keeper.ak, addr, amt) } -// SendCoins moves coins from one account to another -func (keeper BaseKeeper) SendCoins( - ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins, -) (sdk.Tags, sdk.Error) { - - return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) -} - -// InputOutputCoins handles a list of inputs and outputs -func (keeper BaseKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) { - return inputOutputCoins(ctx, keeper.am, inputs, outputs) -} - -//______________________________________________________________________________________________ +//----------------------------------------------------------------------------- +// Send Keeper // SendKeeper defines a module interface that facilitates the transfer of coins // between accounts without the possibility of creating coins. type SendKeeper interface { ViewKeeper + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) } @@ -96,22 +84,17 @@ var _ SendKeeper = (*BaseSendKeeper)(nil) // SendKeeper only allows transfers between accounts without the possibility of // creating coins. It implements the SendKeeper interface. type BaseSendKeeper struct { - am auth.AccountKeeper + BaseViewKeeper + + ak auth.AccountKeeper } // NewBaseSendKeeper returns a new BaseSendKeeper. -func NewBaseSendKeeper(am auth.AccountKeeper) BaseSendKeeper { - return BaseSendKeeper{am: am} -} - -// GetCoins returns the coins at the addr. -func (keeper BaseSendKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { - return getCoins(ctx, keeper.am, addr) -} - -// HasCoins returns whether or not an account has at least amt coins. -func (keeper BaseSendKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool { - return hasCoins(ctx, keeper.am, addr, amt) +func NewBaseSendKeeper(ak auth.AccountKeeper) BaseSendKeeper { + return BaseSendKeeper{ + BaseViewKeeper: NewBaseViewKeeper(ak), + ak: ak, + } } // SendCoins moves coins from one account to another @@ -119,7 +102,7 @@ func (keeper BaseSendKeeper) SendCoins( ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins, ) (sdk.Tags, sdk.Error) { - return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) + return sendCoins(ctx, keeper.ak, fromAddr, toAddr, amt) } // InputOutputCoins handles a list of inputs and outputs @@ -127,10 +110,13 @@ func (keeper BaseSendKeeper) InputOutputCoins( ctx sdk.Context, inputs []Input, outputs []Output, ) (sdk.Tags, sdk.Error) { - return inputOutputCoins(ctx, keeper.am, inputs, outputs) + return inputOutputCoins(ctx, keeper.ak, inputs, outputs) } -//______________________________________________________________________________________________ +//----------------------------------------------------------------------------- +// View Keeper + +var _ ViewKeeper = (*BaseViewKeeper)(nil) // ViewKeeper defines a module interface that facilitates read only access to // account balances. @@ -139,29 +125,29 @@ type ViewKeeper interface { HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool } -var _ ViewKeeper = (*BaseViewKeeper)(nil) - // BaseViewKeeper implements a read only keeper implementation of ViewKeeper. type BaseViewKeeper struct { - am auth.AccountKeeper + ak auth.AccountKeeper } // NewBaseViewKeeper returns a new BaseViewKeeper. -func NewBaseViewKeeper(am auth.AccountKeeper) BaseViewKeeper { - return BaseViewKeeper{am: am} +func NewBaseViewKeeper(ak auth.AccountKeeper) BaseViewKeeper { + return BaseViewKeeper{ + ak: ak, + } } // GetCoins returns the coins at the addr. func (keeper BaseViewKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { - return getCoins(ctx, keeper.am, addr) + return getCoins(ctx, keeper.ak, addr) } // HasCoins returns whether or not an account has at least amt coins. func (keeper BaseViewKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool { - return hasCoins(ctx, keeper.am, addr, amt) + return hasCoins(ctx, keeper.ak, addr, amt) } -//______________________________________________________________________________________________ +//----------------------------------------------------------------------------- func getCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress) sdk.Coins { ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins") diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 535cf036a5..47e9037580 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -20,7 +21,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -62,7 +63,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -79,7 +80,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res = govHandler(ctx, newProposalMsg2) require.True(t, res.IsOK()) @@ -121,7 +122,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { require.False(t, activeQueue.Valid()) activeQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -140,7 +141,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) @@ -163,7 +164,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { require.False(t, activeQueue.Valid()) activeQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -174,7 +175,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)}) + newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 1243bf5590..e134a4a78c 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -4,6 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) // GenesisState - all staking state that must be provided at genesis @@ -43,7 +44,7 @@ func DefaultGenesisState() GenesisState { return GenesisState{ StartingProposalID: 1, DepositParams: DepositParams{ - MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", 10)}, + MinDeposit: sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)}, MaxDepositPeriod: time.Duration(172800) * time.Second, }, VotingParams: VotingParams{ diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 2ff1344b78..23199472ba 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -9,6 +9,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) func TestGetSetProposal(t *testing.T) { @@ -69,14 +70,14 @@ func TestDeposits(t *testing.T) { proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() - fourSteak := sdk.Coins{sdk.NewInt64Coin("steak", 4)} - fiveSteak := sdk.Coins{sdk.NewInt64Coin("steak", 5)} + fourSteak := sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 4)} + fiveSteak := sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 5)} addr0Initial := keeper.ck.GetCoins(ctx, addrs[0]) addr1Initial := keeper.ck.GetCoins(ctx, addrs[1]) - // require.True(t, addr0Initial.IsEqual(sdk.Coins{sdk.NewInt64Coin("steak", 42)})) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin("steak", 42)}, addr0Initial) + // require.True(t, addr0Initial.IsEqual(sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 42)})) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 42)}, addr0Initial) require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{})) diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index bdc273dcd6..e488d2abab 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -7,16 +7,21 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mock" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( - coinsPos = sdk.Coins{sdk.NewInt64Coin("steak", 1000)} + coinsPos = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1000)} coinsZero = sdk.Coins{} - coinsNeg = sdk.Coins{sdk.NewInt64Coin("steak", -10000)} + coinsNeg = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, -10000)} coinsPosNotAtoms = sdk.Coins{sdk.NewInt64Coin("foo", 10000)} - coinsMulti = sdk.Coins{sdk.NewInt64Coin("foo", 10000), sdk.NewInt64Coin("steak", 1000)} + coinsMulti = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1000), sdk.NewInt64Coin("foo", 10000)} ) +func init() { + coinsMulti.Sort() +} + // test ValidateBasic for MsgCreateValidator func TestMsgSubmitProposal(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) @@ -42,9 +47,9 @@ func TestMsgSubmitProposal(t *testing.T) { for i, tc := range tests { msg := NewMsgSubmitProposal(tc.title, tc.description, tc.proposalType, tc.proposerAddr, tc.initialDeposit) if tc.expectPass { - require.Nil(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", i) } else { - require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", i) } } } @@ -68,9 +73,9 @@ func TestMsgDeposit(t *testing.T) { for i, tc := range tests { msg := NewMsgDeposit(tc.depositerAddr, tc.proposalID, tc.depositAmount) if tc.expectPass { - require.Nil(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", i) } else { - require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", i) } } } diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index 0eadc7febb..d075c86aef 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -11,10 +11,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) const ( - denom = "steak" + denom = stakeTypes.DefaultBondDenom ) // SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 26b32cc93a..b2f6aaf32a 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( @@ -25,7 +26,7 @@ func createValidators(t *testing.T, stakeHandler sdk.Handler, ctx sdk.Context, a for i := 0; i < len(addrs); i++ { valCreateMsg := stake.NewMsgCreateValidator( - addrs[i], pubkeys[i], sdk.NewInt64Coin("steak", coinAmt[i]), testDescription, testCommissionMsg, + addrs[i], pubkeys[i], sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, coinAmt[i]), testDescription, testCommissionMsg, ) res := stakeHandler(ctx, valCreateMsg) @@ -289,7 +290,7 @@ func TestTallyDelgatorOverride(t *testing.T) { createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) stake.EndBlocker(ctx, sk) - delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30)) + delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 30)) stakeHandler(ctx, delegator1Msg) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -326,7 +327,7 @@ func TestTallyDelgatorInherit(t *testing.T) { createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) stake.EndBlocker(ctx, sk) - delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30)) + delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 30)) stakeHandler(ctx, delegator1Msg) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -361,9 +362,9 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) stake.EndBlocker(ctx, sk) - delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg) - delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg2) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -393,24 +394,24 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { stakeHandler := stake.NewHandler(sk) val1CreateMsg := stake.NewMsgCreateValidator( - sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 25), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 25), testDescription, testCommissionMsg, ) stakeHandler(ctx, val1CreateMsg) val2CreateMsg := stake.NewMsgCreateValidator( - sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 6), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 6), testDescription, testCommissionMsg, ) stakeHandler(ctx, val2CreateMsg) val3CreateMsg := stake.NewMsgCreateValidator( - sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 7), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 7), testDescription, testCommissionMsg, ) stakeHandler(ctx, val3CreateMsg) - delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg) - delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg2) stake.EndBlocker(ctx, sk) @@ -447,10 +448,10 @@ func TestTallyJailedValidator(t *testing.T) { createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7}) stake.EndBlocker(ctx, sk) - delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg) - delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10)) + delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)) stakeHandler(ctx, delegator1Msg2) val2, found := sk.GetValidator(ctx, sdk.ValAddress(addrs[1])) diff --git a/x/gov/test_common.go b/x/gov/test_common.go index 82ae30358b..4e0ffd51af 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -17,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) // initialize the mock application for this module @@ -44,7 +45,7 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, require.NoError(t, mapp.CompleteSetup(keyStake, tkeyStake, keyGov, keyGlobalParams, tkeyGlobalParams)) - genAccs, addrs, pubKeys, privKeys := mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin("steak", 42)}) + genAccs, addrs, pubKeys, privKeys := mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 42)}) mock.SetGenesis(mapp, genAccs) diff --git a/x/mint/params.go b/x/mint/params.go index 05edc8fd92..47c9c85480 100644 --- a/x/mint/params.go +++ b/x/mint/params.go @@ -2,6 +2,7 @@ package mint import ( "fmt" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -18,7 +19,7 @@ type Params struct { // default minting module parameters func DefaultParams() Params { return Params{ - MintDenom: "steak", + MintDenom: stakeTypes.DefaultBondDenom, InflationRateChange: sdk.NewDecWithPrec(13, 2), InflationMax: sdk.NewDecWithPrec(20, 2), InflationMin: sdk.NewDecWithPrec(7, 2), diff --git a/x/mock/simulation/account.go b/x/mock/simulation/account.go new file mode 100644 index 0000000000..37dfbb2cdb --- /dev/null +++ b/x/mock/simulation/account.go @@ -0,0 +1,51 @@ +package simulation + +import ( + "math/rand" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Account contains a privkey, pubkey, address tuple +// eventually more useful data can be placed in here. +// (e.g. number of coins) +type Account struct { + PrivKey crypto.PrivKey + PubKey crypto.PubKey + Address sdk.AccAddress +} + +// are two accounts equal +func (acc Account) Equals(acc2 Account) bool { + return acc.Address.Equals(acc2.Address) +} + +// RandomAcc pick a random account from an array +func RandomAcc(r *rand.Rand, accs []Account) Account { + return accs[r.Intn( + len(accs), + )] +} + +// RandomAccounts generates n random accounts +func RandomAccounts(r *rand.Rand, n int) []Account { + accs := make([]Account, n) + for i := 0; i < n; i++ { + // don't need that much entropy for simulation + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + useSecp := r.Int63()%2 == 0 + if useSecp { + accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) + } else { + accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + } + return accs +} diff --git a/x/mock/simulation/doc.go b/x/mock/simulation/doc.go index 8b9a3f6932..ff292bd388 100644 --- a/x/mock/simulation/doc.go +++ b/x/mock/simulation/doc.go @@ -2,26 +2,24 @@ Package simulation implements a simulation framework for any state machine built on the SDK which utilizes auth. -It is primarily intended for fuzz testing the integration of modules. -It will test that the provided operations are interoperable, -and that the desired invariants hold. -It can additionally be used to detect what the performance benchmarks in the -system are, by using benchmarking mode and cpu / mem profiling. -If it detects a failure, it provides the entire log of what was ran, +It is primarily intended for fuzz testing the integration of modules. It will +test that the provided operations are interoperable, and that the desired +invariants hold. It can additionally be used to detect what the performance +benchmarks in the system are, by using benchmarking mode and cpu / mem +profiling. If it detects a failure, it provides the entire log of what was ran. -The simulator takes as input: a random seed, the set of operations to run, -the invariants to test, and additional parameters to configure how long to run, -and misc. parameters that affect simulation speed. +The simulator takes as input: a random seed, the set of operations to run, the +invariants to test, and additional parameters to configure how long to run, and +misc. parameters that affect simulation speed. -It is intended that every module provides a list of Operations which will randomly -create and run a message / tx in a manner that is interesting to fuzz, and verify that -the state transition was executed as expected. -Each module should additionally provide methods to assert that the desired invariants hold. +It is intended that every module provides a list of Operations which will +randomly create and run a message / tx in a manner that is interesting to fuzz, +and verify that the state transition was executed as expected. Each module +should additionally provide methods to assert that the desired invariants hold. Then to perform a randomized simulation, select the set of desired operations, -the weightings for each, the invariants you want to test, and how long to run it for. -Then run simulation.Simulate! -The simulator will handle things like ensuring that validators periodically double signing, -or go offline. +the weightings for each, the invariants you want to test, and how long to run +it for. Then run simulation.Simulate! The simulator will handle things like +ensuring that validators periodically double signing, or go offline. */ package simulation diff --git a/x/mock/simulation/event_stats.go b/x/mock/simulation/event_stats.go new file mode 100644 index 0000000000..f54eef4cc9 --- /dev/null +++ b/x/mock/simulation/event_stats.go @@ -0,0 +1,30 @@ +package simulation + +import ( + "fmt" + "sort" +) + +type eventStats map[string]uint + +func newEventStats() eventStats { + events := make(map[string]uint) + return events +} + +func (es eventStats) tally(eventDesc string) { + es[eventDesc]++ +} + +// Pretty-print events as a table +func (es eventStats) Print() { + var keys []string + for key := range es { + keys = append(keys, key) + } + sort.Strings(keys) + fmt.Printf("Event statistics: \n") + for _, key := range keys { + fmt.Printf(" % 60s => %d\n", key, es[key]) + } +} diff --git a/x/mock/simulation/invariants.go b/x/mock/simulation/invariants.go new file mode 100644 index 0000000000..23686005d1 --- /dev/null +++ b/x/mock/simulation/invariants.go @@ -0,0 +1,30 @@ +package simulation + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/baseapp" +) + +// An Invariant is a function which tests a particular invariant. +// If the invariant has been broken, it should return an error +// containing a descriptive message about what happened. +// The simulator will then halt and print the logs. +type Invariant func(app *baseapp.BaseApp) error + +// group of Invarient +type Invariants []Invariant + +// assertAll asserts the all invariants against application state +func (invs Invariants) assertAll(t *testing.T, app *baseapp.BaseApp, + event string, displayLogs func()) { + + for i := 0; i < len(invs); i++ { + if err := invs[i](app); err != nil { + fmt.Printf("Invariants broken after %s\n%s\n", event, err.Error()) + displayLogs() + t.Fatal() + } + } +} diff --git a/x/mock/simulation/mock_tendermint.go b/x/mock/simulation/mock_tendermint.go new file mode 100644 index 0000000000..2ddac2e794 --- /dev/null +++ b/x/mock/simulation/mock_tendermint.go @@ -0,0 +1,201 @@ +package simulation + +import ( + "fmt" + "math/rand" + "sort" + "testing" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" +) + +type mockValidator struct { + val abci.ValidatorUpdate + livenessState int +} + +type mockValidators map[string]mockValidator + +// get mockValidators from abci validators +func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, + params Params) mockValidators { + + validators := make(mockValidators) + for _, validator := range abciVals { + str := fmt.Sprintf("%v", validator.PubKey) + liveliness := GetMemberOfInitialState(r, + params.InitialLivenessWeightings) + + validators[str] = mockValidator{ + val: validator, + livenessState: liveliness, + } + } + + return validators +} + +// TODO describe usage +func (vals mockValidators) getKeys() []string { + keys := make([]string, len(vals)) + i := 0 + for key := range vals { + keys[i] = key + i++ + } + sort.Strings(keys) + return keys +} + +//_________________________________________________________________________________ + +// randomProposer picks a random proposer from the current validator set +func (vals mockValidators) randomProposer(r *rand.Rand) cmn.HexBytes { + keys := vals.getKeys() + if len(keys) == 0 { + return nil + } + key := keys[r.Intn(len(keys))] + proposer := vals[key].val + pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) + if err != nil { + panic(err) + } + return pk.Address() +} + +// updateValidators mimicks Tendermint's update logic +// nolint: unparam +func updateValidators(tb testing.TB, r *rand.Rand, params Params, + current map[string]mockValidator, updates []abci.ValidatorUpdate, + event func(string)) map[string]mockValidator { + + for _, update := range updates { + str := fmt.Sprintf("%v", update.PubKey) + + if update.Power == 0 { + if _, ok := current[str]; !ok { + tb.Fatalf("tried to delete a nonexistent validator") + } + event("endblock/validatorupdates/kicked") + delete(current, str) + + } else if mVal, ok := current[str]; ok { + // validator already exists + mVal.val = update + event("endblock/validatorupdates/updated") + + } else { + // Set this new validator + current[str] = mockValidator{ + update, + GetMemberOfInitialState(r, params.InitialLivenessWeightings), + } + event("endblock/validatorupdates/added") + } + } + + return current +} + +// RandomRequestBeginBlock generates a list of signing validators according to +// the provided list of validators, signing fraction, and evidence fraction +func RandomRequestBeginBlock(r *rand.Rand, params Params, + validators mockValidators, pastTimes []time.Time, + pastVoteInfos [][]abci.VoteInfo, + event func(string), header abci.Header) abci.RequestBeginBlock { + + if len(validators) == 0 { + return abci.RequestBeginBlock{ + Header: header, + } + } + + voteInfos := make([]abci.VoteInfo, len(validators)) + for i, key := range validators.getKeys() { + mVal := validators[key] + mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) + signed := true + + if mVal.livenessState == 1 { + // spotty connection, 50% probability of success + // See https://github.com/golang/go/issues/23804#issuecomment-365370418 + // for reasoning behind computing like this + signed = r.Int63()%2 == 0 + } else if mVal.livenessState == 2 { + // offline + signed = false + } + + if signed { + event("beginblock/signing/signed") + } else { + event("beginblock/signing/missed") + } + + pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) + if err != nil { + panic(err) + } + voteInfos[i] = abci.VoteInfo{ + Validator: abci.Validator{ + Address: pubkey.Address(), + Power: mVal.val.Power, + }, + SignedLastBlock: signed, + } + } + + // return if no past times + if len(pastTimes) <= 0 { + return abci.RequestBeginBlock{ + Header: header, + LastCommitInfo: abci.LastCommitInfo{ + Votes: voteInfos, + }, + } + } + + // TODO: Determine capacity before allocation + evidence := make([]abci.Evidence, 0) + for r.Float64() < params.EvidenceFraction { + + height := header.Height + time := header.Time + vals := voteInfos + + if r.Float64() < params.PastEvidenceFraction { + height = int64(r.Intn(int(header.Height) - 1)) + time = pastTimes[height] + vals = pastVoteInfos[height] + } + validator := vals[r.Intn(len(vals))].Validator + + var totalVotingPower int64 + for _, val := range vals { + totalVotingPower += val.Validator.Power + } + + evidence = append(evidence, + abci.Evidence{ + Type: tmtypes.ABCIEvidenceTypeDuplicateVote, + Validator: validator, + Height: height, + Time: time, + TotalVotingPower: totalVotingPower, + }, + ) + event("beginblock/evidence") + } + + return abci.RequestBeginBlock{ + Header: header, + LastCommitInfo: abci.LastCommitInfo{ + Votes: voteInfos, + }, + ByzantineValidators: evidence, + } +} diff --git a/x/mock/simulation/operation.go b/x/mock/simulation/operation.go new file mode 100644 index 0000000000..00237e03ee --- /dev/null +++ b/x/mock/simulation/operation.go @@ -0,0 +1,112 @@ +package simulation + +import ( + "math/rand" + "sort" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Operation runs a state machine transition, and ensures the transition +// happened as expected. The operation could be running and testing a fuzzed +// transaction, or doing the same for a message. +// +// For ease of debugging, an operation returns a descriptive message "action", +// which details what this fuzzed state machine transition actually did. +// +// Operations can optionally provide a list of "FutureOperations" to run later +// These will be ran at the beginning of the corresponding block. +type Operation func(r *rand.Rand, app *baseapp.BaseApp, + ctx sdk.Context, accounts []Account, event func(string)) ( + action string, futureOps []FutureOperation, err error) + +// queue of operations +type OperationQueue map[int][]Operation + +func newOperationQueue() OperationQueue { + operationQueue := make(OperationQueue) + return operationQueue +} + +// adds all future operations into the operation queue. +func queueOperations(queuedOps OperationQueue, + queuedTimeOps []FutureOperation, futureOps []FutureOperation) { + + if futureOps == nil { + return + } + + for _, futureOp := range futureOps { + if futureOp.BlockHeight != 0 { + if val, ok := queuedOps[futureOp.BlockHeight]; ok { + queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op) + } else { + queuedOps[futureOp.BlockHeight] = []Operation{futureOp.Op} + } + continue + } + + // TODO: Replace with proper sorted data structure, so don't have the + // copy entire slice + index := sort.Search( + len(queuedTimeOps), + func(i int) bool { + return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime) + }, + ) + queuedTimeOps = append(queuedTimeOps, FutureOperation{}) + copy(queuedTimeOps[index+1:], queuedTimeOps[index:]) + queuedTimeOps[index] = futureOp + } +} + +//________________________________________________________________________ + +// FutureOperation is an operation which will be ran at the beginning of the +// provided BlockHeight. If both a BlockHeight and BlockTime are specified, it +// will use the BlockHeight. In the (likely) event that multiple operations +// are queued at the same block height, they will execute in a FIFO pattern. +type FutureOperation struct { + BlockHeight int + BlockTime time.Time + Op Operation +} + +//________________________________________________________________________ + +// WeightedOperation is an operation with associated weight. +// This is used to bias the selection operation within the simulator. +type WeightedOperation struct { + Weight int + Op Operation +} + +// WeightedOperations is the group of all weighted operations to simulate. +type WeightedOperations []WeightedOperation + +func (ops WeightedOperations) totalWeight() int { + totalOpWeight := 0 + for _, op := range ops { + totalOpWeight += op.Weight + } + return totalOpWeight +} + +type selectOpFn func(r *rand.Rand) Operation + +func (ops WeightedOperations) getSelectOpFn() selectOpFn { + totalOpWeight := ops.totalWeight() + return func(r *rand.Rand) Operation { + x := r.Intn(totalOpWeight) + for i := 0; i < len(ops); i++ { + if x <= ops[i].Weight { + return ops[i].Op + } + x -= ops[i].Weight + } + // shouldn't happen + return ops[0].Op + } +} diff --git a/x/mock/simulation/params.go b/x/mock/simulation/params.go index 404a85e544..8499e6c118 100644 --- a/x/mock/simulation/params.go +++ b/x/mock/simulation/params.go @@ -15,15 +15,18 @@ const ( onOperation bool = false ) +// TODO explain transitional matrix usage var ( - // Currently there are 3 different liveness types, fully online, spotty connection, offline. + // Currently there are 3 different liveness types, + // fully online, spotty connection, offline. defaultLivenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{ {90, 20, 1}, {10, 50, 5}, {0, 10, 1000}, }) - // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 + // 3 states: rand in range [0, 4*provided blocksize], + // rand in range [0, 2 * provided blocksize], 0 defaultBlockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ {85, 5, 0}, {15, 92, 1}, diff --git a/x/mock/simulation/rand_util.go b/x/mock/simulation/rand_util.go new file mode 100644 index 0000000000..c40d2a65ca --- /dev/null +++ b/x/mock/simulation/rand_util.go @@ -0,0 +1,64 @@ +package simulation + +import ( + "math/big" + "math/rand" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" +) + +const ( + letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = r.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + return string(b) +} + +// Generate a random amount +func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { + return sdk.NewInt(int64(r.Intn(int(max.Int64())))) +} + +// RandomDecAmount generates a random decimal amount +func RandomDecAmount(r *rand.Rand, max sdk.Dec) sdk.Dec { + randInt := big.NewInt(0).Rand(r, max.Int) + return sdk.NewDecFromBigIntWithPrec(randInt, sdk.Precision) +} + +// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts +func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) { + addrs := make([]sdk.AccAddress, len(accs)) + for i := 0; i < len(accs); i++ { + addrs[i] = accs[i].Address + } + mock.RandomSetGenesis(r, app, addrs, denoms) +} + +// RandTimestamp generates a random timestamp +func RandTimestamp(r *rand.Rand) time.Time { + // json.Marshal breaks for timestamps greater with year greater than 9999 + unixTime := r.Int63n(253373529600) + return time.Unix(unixTime, 0) +} diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go deleted file mode 100644 index a568997e62..0000000000 --- a/x/mock/simulation/random_simulate_blocks.go +++ /dev/null @@ -1,488 +0,0 @@ -package simulation - -import ( - "encoding/json" - "fmt" - "math/rand" - "os" - "os/signal" - "runtime/debug" - "sort" - "strings" - "syscall" - "testing" - "time" - - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Simulate tests application by sending random messages. -func Simulate(t *testing.T, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, - ops []WeightedOperation, setups []RandSetup, - invariants []Invariant, numBlocks int, blockSize int, commit bool) error { - - time := time.Now().UnixNano() - return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) -} - -func initChain(r *rand.Rand, params Params, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) { - res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) - validators = make(map[string]mockValidator) - for _, validator := range res.Validators { - str := fmt.Sprintf("%v", validator.PubKey) - validators[str] = mockValidator{validator, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} - } - - for i := 0; i < len(setups); i++ { - setups[i](r, accounts) - } - - return -} - -func randTimestamp(r *rand.Rand) time.Time { - // json.Marshal breaks for timestamps greater with year greater than 9999 - unixTime := r.Int63n(253373529600) - return time.Unix(unixTime, 0) -} - -// SimulateFromSeed tests an application by running the provided -// operations, testing the provided invariants, but using the provided seed. -func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, - seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, - numBlocks int, blockSize int, commit bool) (simError error) { - - // in case we have to end early, don't os.Exit so that we can run cleanup code. - stopEarly := false - testingMode, t, b := getTestingMode(tb) - fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) - r := rand.New(rand.NewSource(seed)) - params := RandomParams(r) // := DefaultParams() - fmt.Printf("Randomized simulation params: %+v\n", params) - timestamp := randTimestamp(r) - fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) - timeDiff := maxTimePerBlock - minTimePerBlock - - accs := RandomAccounts(r, params.NumKeys) - - // Setup event stats - events := make(map[string]uint) - event := func(what string) { - events[what]++ - } - - validators := initChain(r, params, accs, setups, app, appStateFn) - // Second variable to keep pending validator set (delayed one block since TM 0.24) - // Initially this is the same as the initial validator set - nextValidators := validators - - header := abci.Header{Height: 1, Time: timestamp, ProposerAddress: randomProposer(r, validators)} - opCount := 0 - - // Setup code to catch SIGTERM's - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) - go func() { - receivedSignal := <-c - fmt.Printf("\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) - simError = fmt.Errorf("Exited due to %s", receivedSignal) - stopEarly = true - }() - - var pastTimes []time.Time - var pastVoteInfos [][]abci.VoteInfo - - request := RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, event, header) - // These are operations which have been queued by previous operations - operationQueue := make(map[int][]Operation) - timeOperationQueue := []FutureOperation{} - var blockLogBuilders []*strings.Builder - - if testingMode { - blockLogBuilders = make([]*strings.Builder, numBlocks) - } - displayLogs := logPrinter(testingMode, blockLogBuilders) - blockSimulator := createBlockSimulator(testingMode, tb, t, params, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) - if !testingMode { - b.ResetTimer() - } else { - // Recover logs in case of panic - defer func() { - if r := recover(); r != nil { - fmt.Println("Panic with err\n", r) - stackTrace := string(debug.Stack()) - fmt.Println(stackTrace) - displayLogs() - simError = fmt.Errorf("Simulation halted due to panic on block %d", header.Height) - } - }() - } - - for i := 0; i < numBlocks && !stopEarly; i++ { - // Log the header time for future lookup - pastTimes = append(pastTimes, header.Time) - pastVoteInfos = append(pastVoteInfos, request.LastCommitInfo.Votes) - - // Construct log writer - logWriter := addLogMessage(testingMode, blockLogBuilders, i) - - // Run the BeginBlock handler - logWriter("BeginBlock") - app.BeginBlock(request) - - if testingMode { - // Make sure invariants hold at beginning of block - assertAllInvariants(t, app, invariants, "BeginBlock", displayLogs) - } - - ctx := app.NewContext(false, header) - - // Run queued operations. Ignores blocksize if blocksize is too small - logWriter("Queued operations") - numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter, displayLogs, event) - numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, accs, logWriter, displayLogs, event) - if testingMode && onOperation { - // Make sure invariants hold at end of queued operations - assertAllInvariants(t, app, invariants, "QueuedOperations", displayLogs) - } - - logWriter("Standard operations") - operations := blockSimulator(r, app, ctx, accs, header, logWriter) - opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan - if testingMode { - // Make sure invariants hold at end of block - assertAllInvariants(t, app, invariants, "StandardOperations", displayLogs) - } - - res := app.EndBlock(abci.RequestEndBlock{}) - header.Height++ - header.Time = header.Time.Add(time.Duration(minTimePerBlock) * time.Second).Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second) - header.ProposerAddress = randomProposer(r, validators) - logWriter("EndBlock") - - if testingMode { - // Make sure invariants hold at end of block - assertAllInvariants(t, app, invariants, "EndBlock", displayLogs) - } - if commit { - app.Commit() - } - - if header.ProposerAddress == nil { - fmt.Printf("\nSimulation stopped early as all validators have been unbonded, there is nobody left propose a block!\n") - stopEarly = true - break - } - - // Generate a random RequestBeginBlock with the current validator set for the next block - request = RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, event, header) - - // Update the validator set, which will be reflected in the application on the next block - validators = nextValidators - nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, event) - } - if stopEarly { - DisplayEvents(events) - return - } - fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds), : %v, operations ran %d\n", header.Height, header.Time, opCount) - DisplayEvents(events) - return nil -} - -type blockSimFn func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string), -) (opCount int) - -// Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize -// memory overhead -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params, - event func(string), invariants []Invariant, - ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, - totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { - - var ( - lastBlocksizeState = 0 // state for [4 * uniform distribution] - totalOpWeight = 0 - blocksize int - ) - - for i := 0; i < len(ops); i++ { - totalOpWeight += ops[i].Weight - } - selectOp := func(r *rand.Rand) Operation { - x := r.Intn(totalOpWeight) - for i := 0; i < len(ops); i++ { - if x <= ops[i].Weight { - return ops[i].Op - } - x -= ops[i].Weight - } - // shouldn't happen - return ops[0].Op - } - - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { - fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) - lastBlocksizeState, blocksize = getBlockSize(r, params, lastBlocksizeState, avgBlockSize) - for j := 0; j < blocksize; j++ { - logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) - logWriter(logUpdate) - if err != nil { - displayLogs() - tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err) - } - - queueOperations(operationQueue, timeOperationQueue, futureOps) - if testingMode { - if onOperation { - assertAllInvariants(t, app, invariants, fmt.Sprintf("operation: %v", logUpdate), displayLogs) - } - if opCount%50 == 0 { - fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) - } - } - opCount++ - } - return opCount - } -} - -func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) { - testingMode = false - if _t, ok := tb.(*testing.T); ok { - t = _t - testingMode = true - } else { - b = tb.(*testing.B) - } - return -} - -// getBlockSize returns a block size as determined from the transition matrix. -// It targets making average block size the provided parameter. The three -// states it moves between are: -// "over stuffed" blocks with average size of 2 * avgblocksize, -// normal sized blocks, hitting avgBlocksize on average, -// and empty blocks, with no txs / only txs scheduled from the past. -func getBlockSize(r *rand.Rand, params Params, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { - // TODO: Make default blocksize transition matrix actually make the average - // blocksize equal to avgBlockSize. - state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState) - if state == 0 { - blocksize = r.Intn(avgBlockSize * 4) - } else if state == 1 { - blocksize = r.Intn(avgBlockSize * 2) - } else { - blocksize = 0 - } - return -} - -// adds all future operations into the operation queue. -func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations []FutureOperation, futureOperations []FutureOperation) { - if futureOperations == nil { - return - } - for _, futureOp := range futureOperations { - if futureOp.BlockHeight != 0 { - if val, ok := queuedOperations[futureOp.BlockHeight]; ok { - queuedOperations[futureOp.BlockHeight] = append(val, futureOp.Op) - } else { - queuedOperations[futureOp.BlockHeight] = []Operation{futureOp.Op} - } - } else { - // TODO: Replace with proper sorted data structure, so don't have the copy entire slice - index := sort.Search(len(queuedTimeOperations), func(i int) bool { return queuedTimeOperations[i].BlockTime.After(futureOp.BlockTime) }) - queuedTimeOperations = append(queuedTimeOperations, FutureOperation{}) - copy(queuedTimeOperations[index+1:], queuedTimeOperations[index:]) - queuedTimeOperations[index] = futureOp - } - } -} - -// nolint: errcheck -func runQueuedOperations(queueOperations map[int][]Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { - if queuedOps, ok := queueOperations[height]; ok { - numOps := len(queuedOps) - for i := 0; i < numOps; i++ { - // For now, queued operations cannot queue more operations. - // If a need arises for us to support queued messages to queue more messages, this can - // be changed. - logUpdate, _, err := queuedOps[i](r, app, ctx, accounts, event) - logWriter(logUpdate) - if err != nil { - displayLogs() - tb.FailNow() - } - } - delete(queueOperations, height) - return numOps - } - return 0 -} - -func runQueuedTimeOperations(queueOperations []FutureOperation, currentTime time.Time, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { - - numOpsRan = 0 - for len(queueOperations) > 0 && currentTime.After(queueOperations[0].BlockTime) { - // For now, queued operations cannot queue more operations. - // If a need arises for us to support queued messages to queue more messages, this can - // be changed. - logUpdate, _, err := queueOperations[0].Op(r, app, ctx, accounts, event) - logWriter(logUpdate) - if err != nil { - displayLogs() - tb.FailNow() - } - queueOperations = queueOperations[1:] - numOpsRan++ - } - return numOpsRan -} - -func getKeys(validators map[string]mockValidator) []string { - keys := make([]string, len(validators)) - i := 0 - for key := range validators { - keys[i] = key - i++ - } - sort.Strings(keys) - return keys -} - -// randomProposer picks a random proposer from the current validator set -func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes { - keys := getKeys(validators) - if len(keys) == 0 { - return nil - } - key := keys[r.Intn(len(keys))] - proposer := validators[key].val - pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) - if err != nil { - panic(err) - } - return pk.Address() -} - -// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction -// nolint: unparam -func RandomRequestBeginBlock(r *rand.Rand, params Params, validators map[string]mockValidator, - pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock { - if len(validators) == 0 { - return abci.RequestBeginBlock{Header: header} - } - voteInfos := make([]abci.VoteInfo, len(validators)) - i := 0 - for _, key := range getKeys(validators) { - mVal := validators[key] - mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) - signed := true - - if mVal.livenessState == 1 { - // spotty connection, 50% probability of success - // See https://github.com/golang/go/issues/23804#issuecomment-365370418 - // for reasoning behind computing like this - signed = r.Int63()%2 == 0 - } else if mVal.livenessState == 2 { - // offline - signed = false - } - if signed { - event("beginblock/signing/signed") - } else { - event("beginblock/signing/missed") - } - pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) - if err != nil { - panic(err) - } - voteInfos[i] = abci.VoteInfo{ - Validator: abci.Validator{ - Address: pubkey.Address(), - Power: mVal.val.Power, - }, - SignedLastBlock: signed, - } - i++ - } - // TODO: Determine capacity before allocation - evidence := make([]abci.Evidence, 0) - // Anything but the first block - if len(pastTimes) > 0 { - for r.Float64() < params.EvidenceFraction { - height := header.Height - time := header.Time - vals := voteInfos - if r.Float64() < params.PastEvidenceFraction { - height = int64(r.Intn(int(header.Height) - 1)) - time = pastTimes[height] - vals = pastVoteInfos[height] - } - validator := vals[r.Intn(len(vals))].Validator - var totalVotingPower int64 - for _, val := range vals { - totalVotingPower += val.Validator.Power - } - evidence = append(evidence, abci.Evidence{ - Type: tmtypes.ABCIEvidenceTypeDuplicateVote, - Validator: validator, - Height: height, - Time: time, - TotalVotingPower: totalVotingPower, - }) - event("beginblock/evidence") - } - } - return abci.RequestBeginBlock{ - Header: header, - LastCommitInfo: abci.LastCommitInfo{ - Votes: voteInfos, - }, - ByzantineValidators: evidence, - } -} - -// updateValidators mimicks Tendermint's update logic -// nolint: unparam -func updateValidators(tb testing.TB, r *rand.Rand, params Params, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { - - for _, update := range updates { - str := fmt.Sprintf("%v", update.PubKey) - switch { - case update.Power == 0: - if _, ok := current[str]; !ok { - tb.Fatalf("tried to delete a nonexistent validator") - } - - event("endblock/validatorupdates/kicked") - delete(current, str) - default: - // Does validator already exist? - if mVal, ok := current[str]; ok { - mVal.val = update - event("endblock/validatorupdates/updated") - } else { - // Set this new validator - current[str] = mockValidator{update, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} - event("endblock/validatorupdates/added") - } - } - } - - return current -} diff --git a/x/mock/simulation/simulate.go b/x/mock/simulation/simulate.go new file mode 100644 index 0000000000..ac0e5d3f7f --- /dev/null +++ b/x/mock/simulation/simulate.go @@ -0,0 +1,330 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" + "os/signal" + "runtime/debug" + "strings" + "syscall" + "testing" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RandSetup performs the random setup the mock module needs. +type RandSetup func(r *rand.Rand, accounts []Account) + +// AppStateFn returns the app state json bytes +type AppStateFn func(r *rand.Rand, accs []Account) json.RawMessage + +// Simulate tests application by sending random messages. +func Simulate(t *testing.T, app *baseapp.BaseApp, + appStateFn AppStateFn, ops WeightedOperations, setups []RandSetup, + invariants Invariants, numBlocks int, blockSize int, commit bool) error { + + time := time.Now().UnixNano() + return SimulateFromSeed(t, app, appStateFn, time, ops, + setups, invariants, numBlocks, blockSize, commit) +} + +// initialize the chain for the simulation +func initChain(r *rand.Rand, params Params, accounts []Account, + setups []RandSetup, app *baseapp.BaseApp, + appStateFn AppStateFn) mockValidators { + + req := abci.RequestInitChain{ + AppStateBytes: appStateFn(r, accounts), + } + res := app.InitChain(req) + validators := newMockValidators(r, res.Validators, params) + + for i := 0; i < len(setups); i++ { + setups[i](r, accounts) + } + return validators +} + +// SimulateFromSeed tests an application by running the provided +// operations, testing the provided invariants, but using the provided seed. +// TODO split this monster function up +func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, + appStateFn AppStateFn, seed int64, ops WeightedOperations, + setups []RandSetup, invariants Invariants, + numBlocks int, blockSize int, commit bool) (simError error) { + + // in case we have to end early, don't os.Exit so that we can run cleanup code. + stopEarly := false + testingMode, t, b := getTestingMode(tb) + fmt.Printf("Starting SimulateFromSeed with randomness "+ + "created with seed %d\n", int(seed)) + + r := rand.New(rand.NewSource(seed)) + params := RandomParams(r) // := DefaultParams() + fmt.Printf("Randomized simulation params: %+v\n", params) + + timestamp := RandTimestamp(r) + fmt.Printf("Starting the simulation from time %v, unixtime %v\n", + timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) + + timeDiff := maxTimePerBlock - minTimePerBlock + accs := RandomAccounts(r, params.NumKeys) + eventStats := newEventStats() + + // Second variable to keep pending validator set (delayed one block since + // TM 0.24) Initially this is the same as the initial validator set + validators := initChain(r, params, accs, setups, app, appStateFn) + nextValidators := validators + + header := abci.Header{ + Height: 1, + Time: timestamp, + ProposerAddress: validators.randomProposer(r), + } + opCount := 0 + + // Setup code to catch SIGTERM's + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) + go func() { + receivedSignal := <-c + fmt.Printf("\nExiting early due to %s, on block %d, operation %d\n", + receivedSignal, header.Height, opCount) + simError = fmt.Errorf("Exited due to %s", receivedSignal) + stopEarly = true + }() + + var pastTimes []time.Time + var pastVoteInfos [][]abci.VoteInfo + + request := RandomRequestBeginBlock(r, params, + validators, pastTimes, pastVoteInfos, eventStats.tally, header) + + // These are operations which have been queued by previous operations + operationQueue := newOperationQueue() + timeOperationQueue := []FutureOperation{} + var blockLogBuilders []*strings.Builder + + if testingMode { + blockLogBuilders = make([]*strings.Builder, numBlocks) + } + displayLogs := logPrinter(testingMode, blockLogBuilders) + blockSimulator := createBlockSimulator( + testingMode, tb, t, params, eventStats.tally, invariants, + ops, operationQueue, timeOperationQueue, + numBlocks, blockSize, displayLogs) + + if !testingMode { + b.ResetTimer() + } else { + // Recover logs in case of panic + defer func() { + if r := recover(); r != nil { + fmt.Println("Panic with err\n", r) + stackTrace := string(debug.Stack()) + fmt.Println(stackTrace) + displayLogs() + simError = fmt.Errorf( + "Simulation halted due to panic on block %d", + header.Height) + } + }() + } + + // TODO split up the contents of this for loop into new functions + for i := 0; i < numBlocks && !stopEarly; i++ { + + // Log the header time for future lookup + pastTimes = append(pastTimes, header.Time) + pastVoteInfos = append(pastVoteInfos, request.LastCommitInfo.Votes) + + // Construct log writer + logWriter := addLogMessage(testingMode, blockLogBuilders, i) + + // Run the BeginBlock handler + logWriter("BeginBlock") + app.BeginBlock(request) + + if testingMode { + invariants.assertAll(t, app, "BeginBlock", displayLogs) + } + + ctx := app.NewContext(false, header) + + // Run queued operations. Ignores blocksize if blocksize is too small + logWriter("Queued operations") + numQueuedOpsRan := runQueuedOperations( + operationQueue, int(header.Height), + tb, r, app, ctx, accs, logWriter, + displayLogs, eventStats.tally) + + numQueuedTimeOpsRan := runQueuedTimeOperations( + timeOperationQueue, header.Time, + tb, r, app, ctx, accs, + logWriter, displayLogs, eventStats.tally) + + if testingMode && onOperation { + invariants.assertAll(t, app, "QueuedOperations", displayLogs) + } + + logWriter("Standard operations") + operations := blockSimulator(r, app, ctx, accs, header, logWriter) + opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan + if testingMode { + invariants.assertAll(t, app, "StandardOperations", displayLogs) + } + + res := app.EndBlock(abci.RequestEndBlock{}) + header.Height++ + header.Time = header.Time.Add( + time.Duration(minTimePerBlock) * time.Second) + header.Time = header.Time.Add( + time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second) + header.ProposerAddress = validators.randomProposer(r) + logWriter("EndBlock") + + if testingMode { + invariants.assertAll(t, app, "EndBlock", displayLogs) + } + if commit { + app.Commit() + } + + if header.ProposerAddress == nil { + fmt.Printf("\nSimulation stopped early as all validators " + + "have been unbonded, there is nobody left propose a block!\n") + stopEarly = true + break + } + + // Generate a random RequestBeginBlock with the current validator set + // for the next block + request = RandomRequestBeginBlock(r, params, validators, + pastTimes, pastVoteInfos, eventStats.tally, header) + + // Update the validator set, which will be reflected in the application + // on the next block + validators = nextValidators + nextValidators = updateValidators(tb, r, params, + validators, res.ValidatorUpdates, eventStats.tally) + } + + if stopEarly { + eventStats.Print() + return simError + } + fmt.Printf("\nSimulation complete. Final height (blocks): %d, "+ + "final time (seconds), : %v, operations ran %d\n", + header.Height, header.Time, opCount) + + eventStats.Print() + return nil +} + +//______________________________________________________________________________ + +type blockSimFn func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) + +// Returns a function to simulate blocks. Written like this to avoid constant +// parameters being passed everytime, to minimize memory overhead. +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params, + event func(string), invariants Invariants, ops WeightedOperations, + operationQueue OperationQueue, timeOperationQueue []FutureOperation, + totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { + + var lastBlocksizeState = 0 // state for [4 * uniform distribution] + var blocksize int + selectOp := ops.getSelectOpFn() + + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + + fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", + header.Height, totalNumBlocks, opCount, blocksize) + lastBlocksizeState, blocksize = getBlockSize(r, params, lastBlocksizeState, avgBlockSize) + + for i := 0; i < blocksize; i++ { + + logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) + logWriter(logUpdate) + if err != nil { + displayLogs() + tb.Fatalf("error on operation %d within block %d, %v", + header.Height, opCount, err) + } + + queueOperations(operationQueue, timeOperationQueue, futureOps) + if testingMode { + if onOperation { + eventStr := fmt.Sprintf("operation: %v", logUpdate) + invariants.assertAll(t, app, eventStr, displayLogs) + } + if opCount%50 == 0 { + fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", + header.Height, totalNumBlocks, opCount, blocksize) + } + } + opCount++ + } + return opCount + } +} + +// nolint: errcheck +func runQueuedOperations(queueOps map[int][]Operation, + height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, + ctx sdk.Context, accounts []Account, logWriter func(string), + displayLogs func(), tallyEvent func(string)) (numOpsRan int) { + + queuedOp, ok := queueOps[height] + if !ok { + return 0 + } + + numOpsRan = len(queuedOp) + for i := 0; i < numOpsRan; i++ { + + // For now, queued operations cannot queue more operations. + // If a need arises for us to support queued messages to queue more messages, this can + // be changed. + logUpdate, _, err := queuedOp[i](r, app, ctx, accounts, tallyEvent) + logWriter(logUpdate) + if err != nil { + displayLogs() + tb.FailNow() + } + } + delete(queueOps, height) + return numOpsRan +} + +func runQueuedTimeOperations(queueOps []FutureOperation, + currentTime time.Time, tb testing.TB, r *rand.Rand, + app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, + logWriter func(string), displayLogs func(), tallyEvent func(string)) (numOpsRan int) { + + numOpsRan = 0 + for len(queueOps) > 0 && currentTime.After(queueOps[0].BlockTime) { + + // For now, queued operations cannot queue more operations. + // If a need arises for us to support queued messages to queue more messages, this can + // be changed. + logUpdate, _, err := queueOps[0].Op(r, app, ctx, accounts, tallyEvent) + logWriter(logUpdate) + if err != nil { + displayLogs() + tb.FailNow() + } + + queueOps = queueOps[1:] + numOpsRan++ + } + return numOpsRan +} diff --git a/x/mock/simulation/transition_matrix.go b/x/mock/simulation/transition_matrix.go index 39bdb1e4f9..f7d713775d 100644 --- a/x/mock/simulation/transition_matrix.go +++ b/x/mock/simulation/transition_matrix.go @@ -5,12 +5,11 @@ import ( "math/rand" ) -// TransitionMatrix is _almost_ a left stochastic matrix. -// It is technically not one due to not normalizing the column values. -// In the future, if we want to find the steady state distribution, -// it will be quite easy to normalize these values to get a stochastic matrix. -// Floats aren't currently used as the default due to non-determinism across -// architectures +// TransitionMatrix is _almost_ a left stochastic matrix. It is technically +// not one due to not normalizing the column values. In the future, if we want +// to find the steady state distribution, it will be quite easy to normalize +// these values to get a stochastic matrix. Floats aren't currently used as +// the default due to non-determinism across architectures type TransitionMatrix struct { weights [][]int // total in each column @@ -24,7 +23,8 @@ func CreateTransitionMatrix(weights [][]int) (TransitionMatrix, error) { n := len(weights) for i := 0; i < n; i++ { if len(weights[i]) != n { - return TransitionMatrix{}, fmt.Errorf("Transition Matrix: Non-square matrix provided, error on row %d", i) + return TransitionMatrix{}, + fmt.Errorf("Transition Matrix: Non-square matrix provided, error on row %d", i) } } totals := make([]int, n) @@ -36,8 +36,8 @@ func CreateTransitionMatrix(weights [][]int) (TransitionMatrix, error) { return TransitionMatrix{weights, totals, n}, nil } -// NextState returns the next state randomly chosen using r, and the weightings provided -// in the transition matrix. +// NextState returns the next state randomly chosen using r, and the weightings +// provided in the transition matrix. func (t TransitionMatrix) NextState(r *rand.Rand, i int) int { randNum := r.Intn(t.totals[i]) for row := 0; row < t.n; row++ { diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go deleted file mode 100644 index e601f2e1f9..0000000000 --- a/x/mock/simulation/types.go +++ /dev/null @@ -1,87 +0,0 @@ -package simulation - -import ( - "math/rand" - "time" - - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" -) - -type ( - // Operation runs a state machine transition, - // and ensures the transition happened as expected. - // The operation could be running and testing a fuzzed transaction, - // or doing the same for a message. - // - // For ease of debugging, - // an operation returns a descriptive message "action", - // which details what this fuzzed state machine transition actually did. - // - // Operations can optionally provide a list of "FutureOperations" to run later - // These will be ran at the beginning of the corresponding block. - Operation func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, event func(string), - ) (action string, futureOperations []FutureOperation, err error) - - // RandSetup performs the random setup the mock module needs. - RandSetup func(r *rand.Rand, accounts []Account) - - // An Invariant is a function which tests a particular invariant. - // If the invariant has been broken, it should return an error - // containing a descriptive message about what happened. - // The simulator will then halt and print the logs. - Invariant func(app *baseapp.BaseApp) error - - // Account contains a privkey, pubkey, address tuple - // eventually more useful data can be placed in here. - // (e.g. number of coins) - Account struct { - PrivKey crypto.PrivKey - PubKey crypto.PubKey - Address sdk.AccAddress - } - - mockValidator struct { - val abci.ValidatorUpdate - livenessState int - } - - // FutureOperation is an operation which will be ran at the - // beginning of the provided BlockHeight. - // If both a BlockHeight and BlockTime are specified, it will use the BlockHeight. - // In the (likely) event that multiple operations are queued at the same - // block height, they will execute in a FIFO pattern. - FutureOperation struct { - BlockHeight int - BlockTime time.Time - Op Operation - } - - // WeightedOperation is an operation with associated weight. - // This is used to bias the selection operation within the simulator. - WeightedOperation struct { - Weight int - Op Operation - } -) - -// TODO remove? not being called anywhere -// PeriodicInvariant returns an Invariant function closure that asserts -// a given invariant if the mock application's last block modulo the given -// period is congruent to the given offset. -func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant { - return func(app *baseapp.BaseApp) error { - if int(app.LastBlockHeight())%period == offset { - return invariant(app) - } - return nil - } -} - -// nolint -func (acc Account) Equals(acc2 Account) bool { - return acc.Address.Equals(acc2.Address) -} diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index 62b253a258..df2b6dae44 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -2,170 +2,121 @@ package simulation import ( "fmt" - "math/big" "math/rand" "os" - "sort" "strings" "testing" "time" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" ) -// shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 -// TODO we should probably move this to tendermint/libs/common/random.go - -const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { - if remain == 0 { - cache, remain = r.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytes) { - b[i] = letterBytes[idx] - i-- - } - cache >>= letterIdxBits - remain-- +func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) { + testingMode = false + if _t, ok := tb.(*testing.T); ok { + t = _t + testingMode = true + } else { + b = tb.(*testing.B) } - return string(b) -} - -// Pretty-print events as a table -func DisplayEvents(events map[string]uint) { - var keys []string - for key := range events { - keys = append(keys, key) - } - sort.Strings(keys) - fmt.Printf("Event statistics: \n") - for _, key := range keys { - fmt.Printf(" % 60s => %d\n", key, events[key]) - } -} - -// RandomAcc pick a random account from an array -func RandomAcc(r *rand.Rand, accs []Account) Account { - return accs[r.Intn( - len(accs), - )] -} - -// Generate a random amount -func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { - return sdk.NewInt(int64(r.Intn(int(max.Int64())))) -} - -// RandomDecAmount generates a random decimal amount -func RandomDecAmount(r *rand.Rand, max sdk.Dec) sdk.Dec { - randInt := big.NewInt(0).Rand(r, max.Int) - return sdk.NewDecFromBigIntWithPrec(randInt, sdk.Precision) -} - -// RandomAccounts generates n random accounts -func RandomAccounts(r *rand.Rand, n int) []Account { - accs := make([]Account, n) - for i := 0; i < n; i++ { - // don't need that much entropy for simulation - privkeySeed := make([]byte, 15) - r.Read(privkeySeed) - useSecp := r.Int63()%2 == 0 - if useSecp { - accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) - } else { - accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed) - } - accs[i].PubKey = accs[i].PrivKey.PubKey() - accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) - } - return accs + return } // Builds a function to add logs for this particular block -func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height int) func(string) { - if testingmode { - blockLogBuilders[height] = &strings.Builder{} - return func(x string) { - (*blockLogBuilders[height]).WriteString(x) - (*blockLogBuilders[height]).WriteString("\n") - } - } - return func(x string) {} -} +func addLogMessage(testingmode bool, + blockLogBuilders []*strings.Builder, height int) func(string) { -// assertAllInvariants asserts a list of provided invariants against application state -func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, - invariants []Invariant, where string, displayLogs func()) { - - for i := 0; i < len(invariants); i++ { - err := invariants[i](app) - if err != nil { - fmt.Printf("Invariants broken after %s\n", where) - fmt.Println(err.Error()) - displayLogs() - t.Fatal() - } + if !testingmode { + return func(_ string) {} } -} -// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts -func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) { - addrs := make([]sdk.AccAddress, len(accs)) - for i := 0; i < len(accs); i++ { - addrs[i] = accs[i].Address + blockLogBuilders[height] = &strings.Builder{} + return func(x string) { + (*blockLogBuilders[height]).WriteString(x) + (*blockLogBuilders[height]).WriteString("\n") } - mock.RandomSetGenesis(r, app, addrs, denoms) } // Creates a function to print out the logs func logPrinter(testingmode bool, logs []*strings.Builder) func() { - if testingmode { - return func() { - numLoggers := 0 - for i := 0; i < len(logs); i++ { - // We're passed the last created block - if logs[i] == nil { - numLoggers = i - break - } + if !testingmode { + return func() {} + } + + return func() { + numLoggers := 0 + for i := 0; i < len(logs); i++ { + // We're passed the last created block + if logs[i] == nil { + numLoggers = i + break } - var f *os.File - if numLoggers > 10 { - fileName := fmt.Sprintf("simulation_log_%s.txt", time.Now().Format("2006-01-02 15:04:05")) - fmt.Printf("Too many logs to display, instead writing to %s\n", fileName) - f, _ = os.Create(fileName) + } + + var f *os.File + if numLoggers > 10 { + fileName := fmt.Sprintf("simulation_log_%s.txt", + time.Now().Format("2006-01-02 15:04:05")) + fmt.Printf("Too many logs to display, instead writing to %s\n", + fileName) + f, _ = os.Create(fileName) + } + + for i := 0; i < numLoggers; i++ { + if f == nil { + fmt.Printf("Begin block %d\n", i+1) + fmt.Println((*logs[i]).String()) + continue } - for i := 0; i < numLoggers; i++ { - if f != nil { - _, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i+1)) - if err != nil { - panic("Failed to write logs to file") - } - _, err = f.WriteString((*logs[i]).String()) - if err != nil { - panic("Failed to write logs to file") - } - } else { - fmt.Printf("Begin block %d\n", i+1) - fmt.Println((*logs[i]).String()) - } + + _, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i+1)) + if err != nil { + panic("Failed to write logs to file") + } + + _, err = f.WriteString((*logs[i]).String()) + if err != nil { + panic("Failed to write logs to file") } } } - return func() {} +} + +// getBlockSize returns a block size as determined from the transition matrix. +// It targets making average block size the provided parameter. The three +// states it moves between are: +// - "over stuffed" blocks with average size of 2 * avgblocksize, +// - normal sized blocks, hitting avgBlocksize on average, +// - and empty blocks, with no txs / only txs scheduled from the past. +func getBlockSize(r *rand.Rand, params Params, + lastBlockSizeState, avgBlockSize int) (state, blocksize int) { + + // TODO: Make default blocksize transition matrix actually make the average + // blocksize equal to avgBlockSize. + state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState) + switch state { + case 0: + blocksize = r.Intn(avgBlockSize * 4) + case 1: + blocksize = r.Intn(avgBlockSize * 2) + default: + blocksize = 0 + } + return state, blocksize +} + +// PeriodicInvariant returns an Invariant function closure that asserts a given +// invariant if the mock application's last block modulo the given period is +// congruent to the given offset. +// +// NOTE this function is intended to be used manually used while running +// computationally heavy simulations. +// TODO reference this function in the codebase probably through use of a switch +func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant { + return func(app *baseapp.BaseApp) error { + if int(app.LastBlockHeight())%period == offset { + return invariant(app) + } + return nil + } } diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 6529609ba0..dd96ff51e2 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -3,15 +3,17 @@ package slashing import ( "testing" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( @@ -93,8 +95,8 @@ func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper, func TestSlashingMsgs(t *testing.T) { mapp, stakeKeeper, keeper := getMockApp(t) - genCoin := sdk.NewInt64Coin("steak", 42) - bondCoin := sdk.NewInt64Coin("steak", 10) + genCoin := sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 42) + bondCoin := sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10) acc1 := &auth.BaseAccount{ Address: addr1, diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 239ae13d62..72ee58e1b2 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -21,6 +21,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/stake" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) // TODO remove dependencies on staking (should only refer to validator set type from sdk) @@ -120,7 +121,7 @@ func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt DelegatorAddr: sdk.AccAddress(address), ValidatorAddr: address, PubKey: pubKey, - Delegation: sdk.NewCoin("steak", amt), + Delegation: sdk.NewCoin(stakeTypes.DefaultBondDenom, amt), } } @@ -128,6 +129,6 @@ func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmoun return stake.MsgDelegate{ DelegatorAddr: delAddr, ValidatorAddr: valAddr, - Delegation: sdk.NewCoin("steak", delAmount), + Delegation: sdk.NewCoin(stakeTypes.DefaultBondDenom, delAmount), } } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 9a38d2d170..b24018a6ce 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -3,13 +3,15 @@ package stake import ( "testing" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/params" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) // getMockApp returns an initialized mock application for this module. @@ -100,8 +102,8 @@ func checkDelegation( func TestStakeMsgs(t *testing.T) { mApp, keeper := getMockApp(t) - genCoin := sdk.NewInt64Coin("steak", 42) - bondCoin := sdk.NewInt64Coin("steak", 10) + genCoin := sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 42) + bondCoin := sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10) acc1 := &auth.BaseAccount{ Address: addr1, diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 3b8d8e2009..a42fcf6d02 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -210,26 +211,11 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { return cmd } -// GetCmdRedelegate implements the redelegate validator command. +// GetCmdRedelegate the begin redelegation command. func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "redelegate", Short: "redelegate illiquid tokens from one validator to another", - } - - cmd.AddCommand( - client.PostCommands( - GetCmdBeginRedelegate(storeName, cdc), - )...) - - return cmd -} - -// GetCmdBeginRedelegate the begin redelegation command. -func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "begin", - Short: "begin redelegation", RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -284,22 +270,7 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "unbond", - Short: "begin or complete unbonding shares from a validator", - } - - cmd.AddCommand( - client.PostCommands( - GetCmdBeginUnbonding(storeName, cdc), - )...) - - return cmd -} - -// GetCmdBeginUnbonding implements the begin unbonding validator command. -func GetCmdBeginUnbonding(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "begin", - Short: "begin unbonding", + Short: "unbond shares from a validator", RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 6c037049d6..3fa641fd2a 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -142,7 +142,7 @@ func TestUnbondingDelegation(t *testing.T) { ValidatorAddr: addrVals[0], CreationHeight: 0, MinTime: time.Unix(0, 0), - Balance: sdk.NewInt64Coin("steak", 5), + Balance: sdk.NewInt64Coin(types.DefaultBondDenom, 5), } // set and retrieve a record @@ -152,7 +152,7 @@ func TestUnbondingDelegation(t *testing.T) { require.True(t, ubd.Equal(resUnbond)) // modify a records, save, and retrieve - ubd.Balance = sdk.NewInt64Coin("steak", 21) + ubd.Balance = sdk.NewInt64Coin(types.DefaultBondDenom, 21) keeper.SetUnbondingDelegation(ctx, ubd) resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/queryable_test.go index 473b65875f..b2b39ff43d 100644 --- a/x/stake/querier/queryable_test.go +++ b/x/stake/querier/queryable_test.go @@ -197,7 +197,7 @@ func TestQueryDelegation(t *testing.T) { pool = keeper.GetPool(ctx) keeper.SetValidatorByPowerIndex(ctx, val2, pool) - keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true) + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(20)), val1, true) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) @@ -377,7 +377,7 @@ func TestQueryRedelegations(t *testing.T) { keeper.SetValidator(ctx, val1) keeper.SetValidator(ctx, val2) - keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(100)), val1, true) + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(100)), val1, true) keeper.ApplyAndReturnValidatorSetUpdates(ctx) keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), sdk.NewDec(20)) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 819bc8b379..439f40de3b 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/keeper" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -48,7 +49,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, loose := sdk.ZeroDec() bonded := sdk.ZeroDec() am.IterateAccounts(ctx, func(acc auth.Account) bool { - loose = loose.Add(sdk.NewDecFromInt(acc.GetCoins().AmountOf("steak"))) + loose = loose.Add(sdk.NewDecFromInt(acc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom))) return false }) k.IterateUnbondingDelegations(ctx, func(_ int64, ubd stake.UnbondingDelegation) bool { @@ -70,19 +71,19 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, feePool := d.GetFeePool(ctx) // add outstanding fees - loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf("steak"))) + loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf(stakeTypes.DefaultBondDenom))) // add community pool - loose = loose.Add(feePool.CommunityPool.AmountOf("steak")) + loose = loose.Add(feePool.CommunityPool.AmountOf(stakeTypes.DefaultBondDenom)) // add validator distribution pool - loose = loose.Add(feePool.ValPool.AmountOf("steak")) + loose = loose.Add(feePool.ValPool.AmountOf(stakeTypes.DefaultBondDenom)) // add validator distribution commission and yet-to-be-withdrawn-by-delegators d.IterateValidatorDistInfos(ctx, func(_ int64, distInfo distribution.ValidatorDistInfo) (stop bool) { - loose = loose.Add(distInfo.DelPool.AmountOf("steak")) - loose = loose.Add(distInfo.ValCommission.AmountOf("steak")) + loose = loose.Add(distInfo.DelPool.AmountOf(stakeTypes.DefaultBondDenom)) + loose = loose.Add(distInfo.ValCommission.AmountOf(stakeTypes.DefaultBondDenom)) return false }, ) diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index ec2dffd927..dda344ffbd 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -26,11 +26,11 @@ func SimulateMsgCreateValidator(m auth.AccountKeeper, k stake.Keeper) simulation Moniker: simulation.RandStringOfLength(r, 10), } - maxCommission := sdk.NewInt(10) + maxCommission := sdk.NewDecWithPrec(r.Int63n(1000), 3) commission := stake.NewCommissionMsg( - sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1), - sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1), - sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1), + simulation.RandomDecAmount(r, maxCommission), + maxCommission, + simulation.RandomDecAmount(r, maxCommission), ) acc := simulation.RandomAcc(r, accs) @@ -85,11 +85,10 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { Details: simulation.RandStringOfLength(r, 10), } - maxCommission := sdk.NewInt(10) - newCommissionRate := sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1) - val := keeper.RandomValidator(r, k, ctx) address := val.GetOperator() + newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate) + msg := stake.MsgEditValidator{ Description: description, ValidatorAddr: address, diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 49bac0f316..88077d18bc 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -28,7 +28,7 @@ var ( func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator { return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commissionMsg, + address, pubKey, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), Description{}, commissionMsg, ) } @@ -38,7 +38,7 @@ func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey cryp commission := NewCommissionMsg(commissionRate, sdk.OneDec(), sdk.ZeroDec()) return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commission, + address, pubKey, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), Description{}, commission, ) } @@ -46,7 +46,7 @@ func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int6 return MsgDelegate{ DelegatorAddr: delAddr, ValidatorAddr: valAddr, - Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), + Delegation: sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), } } @@ -57,6 +57,6 @@ func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.Val DelegatorAddr: delAddr, ValidatorAddr: valAddr, PubKey: valPubKey, - Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), + Delegation: sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), } } diff --git a/x/stake/types/commission.go b/x/stake/types/commission.go index b76971faa0..730bb67413 100644 --- a/x/stake/types/commission.go +++ b/x/stake/types/commission.go @@ -11,7 +11,7 @@ type ( // Commission defines a commission parameters for a given validator. Commission struct { Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators - MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which validator can ever charge + MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which this validator can ever charge MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission UpdateTime time.Time `json:"update_time"` // the last time the commission rate was changed } diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index 7c1800b8e4..7e719f3eef 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -10,9 +10,9 @@ import ( ) var ( - coinPos = sdk.NewInt64Coin("steak", 1000) - coinZero = sdk.NewInt64Coin("steak", 0) - coinNeg = sdk.NewInt64Coin("steak", -10000) + coinPos = sdk.NewInt64Coin(DefaultBondDenom, 1000) + coinZero = sdk.NewInt64Coin(DefaultBondDenom, 0) + coinNeg = sdk.NewInt64Coin(DefaultBondDenom, -10000) ) // test ValidateBasic for MsgCreateValidator diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 699758ace4..4e9aba5ab0 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -19,6 +19,9 @@ const ( // if this is 1, the validator set at the end of a block will sign the block after the next. // Constant as this should not change without a hard fork. ValidatorUpdateDelay int64 = 1 + + // Default bondable coin denomination + DefaultBondDenom = "STAKE" ) // nolint - Keys for parameter access @@ -59,7 +62,7 @@ func DefaultParams() Params { return Params{ UnbondingTime: defaultUnbondingTime, MaxValidators: 100, - BondDenom: "steak", + BondDenom: DefaultBondDenom, } } @@ -69,7 +72,7 @@ func (p Params) HumanReadableString() string { resp := "Params \n" resp += fmt.Sprintf("Unbonding Time: %s\n", p.UnbondingTime) - resp += fmt.Sprintf("Max Validators: %d: \n", p.MaxValidators) + resp += fmt.Sprintf("Max Validators: %d\n", p.MaxValidators) resp += fmt.Sprintf("Bonded Coin Denomination: %s\n", p.BondDenom) return resp }