From 855e0ac70a6574697226b629216b25e0578f5321 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Sun, 28 Oct 2018 18:28:12 -0300 Subject: [PATCH 01/61] Merge PR #2596: Cmds for validator unbondings and redelegations --- PENDING.md | 1 + cmd/gaia/cli_test/cli_test.go | 24 +++++++++ cmd/gaia/cmd/gaiacli/main.go | 29 +++++----- docs/sdk/clients.md | 16 +++++- examples/basecoin/cmd/basecli/main.go | 2 + x/stake/client/cli/query.go | 77 ++++++++++++++++++++++++++- 6 files changed, 133 insertions(+), 16 deletions(-) diff --git a/PENDING.md b/PENDING.md index f315af561e..aa7cca2c4a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,6 +18,7 @@ FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations * Gaia diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index c375bcf4ec..ad1549641b 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -285,6 +285,12 @@ func TestGaiaCLICreateValidator(t *testing.T) { validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, "1.0000000000", validator.Tokens.String()) + validatorUbds := executeGetValidatorUnbondingDelegations(t, + fmt.Sprintf("gaiacli query unbonding-delegations-from %s --output=json %v", + sdk.ValAddress(barAddr), flags)) + require.Len(t, validatorUbds, 1) + require.Equal(t, "1", validatorUbds[0].Balance.Amount.String()) + params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags)) require.True(t, defaultParams.Equal(params)) @@ -693,6 +699,24 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { return validator } +func executeGetValidatorUnbondingDelegations(t *testing.T, cmdStr string) []stake.UnbondingDelegation { + out, _ := tests.ExecuteT(t, cmdStr, "") + var ubds []stake.UnbondingDelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &ubds) + require.NoError(t, err, "out %v\n, err %v", out, err) + return ubds +} + +func executeGetValidatorRedelegations(t *testing.T, cmdStr string) []stake.Redelegation { + out, _ := tests.ExecuteT(t, cmdStr, "") + var reds []stake.Redelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &reds) + require.NoError(t, err, "out %v\n, err %v", out, err) + return reds +} + func executeGetPool(t *testing.T, cmdStr string) stake.Pool { out, _ := tests.ExecuteT(t, cmdStr, "") var pool stake.Pool diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index a2804706b7..6f11e68a5a 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -28,10 +28,11 @@ import ( ) const ( - storeAcc = "acc" - storeGov = "gov" - storeSlashing = "slashing" - storeStake = "stake" + storeAcc = "acc" + storeGov = "gov" + storeSlashing = "slashing" + storeStake = "stake" + queryRouteStake = "stake" ) // rootCmd is the entry point for this binary @@ -70,21 +71,23 @@ func main() { authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)), stakecmd.GetCmdQueryDelegation(storeStake, cdc), stakecmd.GetCmdQueryDelegations(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryRedelegation(storeStake, cdc), + stakecmd.GetCmdQueryRedelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidator(storeStake, cdc), + stakecmd.GetCmdQueryValidators(storeStake, cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc), + stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc), stakecmd.GetCmdQueryParams(storeStake, cdc), stakecmd.GetCmdQueryPool(storeStake, cdc), govcmd.GetCmdQueryProposal(storeGov, cdc), govcmd.GetCmdQueryProposals(storeGov, cdc), - govcmd.GetCmdQueryDeposit(storeGov, cdc), - govcmd.GetCmdQueryDeposits(storeGov, cdc), - stakecmd.GetCmdQueryRedelegation(storeStake, cdc), - stakecmd.GetCmdQueryRedelegations(storeStake, cdc), - slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), - stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidator(storeStake, cdc), - stakecmd.GetCmdQueryValidators(storeStake, cdc), govcmd.GetCmdQueryVote(storeGov, cdc), govcmd.GetCmdQueryVotes(storeGov, cdc), + govcmd.GetCmdQueryDeposit(storeGov, cdc), + govcmd.GetCmdQueryDeposits(storeGov, cdc), + slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), )...) //Add query commands diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 4703c49218..f784dd238d 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -272,7 +272,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va gaiacli query unbonding-delegations ``` -You can also get previous unbonding-delegation(s) status by adding the `--height` flag. +Additionally, as you can get all the unbonding-delegations from a particular validator: + +```bash + gaiacli query unbonding-delegations-from +``` + +To get previous unbonding-delegation(s) status on past blocks, try adding the `--height` flag. #### Redelegate Tokens @@ -315,7 +321,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va gaiacli query redelegations ``` -You can also get previous redelegation(s) status by adding the `--height` flag. +Additionally, as you can get all the outgoing redelegations from a particular validator: + +```bash + gaiacli query redelegations-from +``` + +To get previous redelegation(s) status on past blocks, try adding the `--height` flag. ### Governance diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 94c5c6e016..1732e82eb9 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -49,6 +49,8 @@ func main() { client.GetCommands( stakecmd.GetCmdQueryValidator("stake", cdc), stakecmd.GetCmdQueryValidators("stake", cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations("stake", cdc), + stakecmd.GetCmdQueryValidatorRedelegations("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), stakecmd.GetCmdQueryPool("stake", cdc), diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index ad8030c931..28d09df8ef 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -114,6 +114,80 @@ func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } +// GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. +func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "unbonding-delegations-from [operator-addr]", + Short: "Query all unbonding delegatations from a validator", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + params := stake.QueryValidatorParams{ + ValidatorAddr: valAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validatorUnbondingDelegations", queryRoute), + bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + +// GetCmdQueryValidatorRedelegations implements the query all redelegatations from a validator command. +func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "redelegations-from [operator-addr]", + Short: "Query all outgoing redelegatations from a validator", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + params := stake.QueryValidatorParams{ + ValidatorAddr: valAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validatorRedelegations", queryRoute), + bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + // GetCmdQueryDelegation the query delegation command. func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ @@ -139,6 +213,7 @@ func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { } // parse out the delegation + delegation, err := types.UnmarshalDelegation(cdc, key, res) if err != nil { return err @@ -292,7 +367,7 @@ func GetCmdQueryUnbondingDelegations(storeName string, cdc *codec.Codec) *cobra. return err } - // parse out the validators + // parse out the unbonding delegations var ubds []stake.UnbondingDelegation for _, kv := range resKVs { ubd := types.MustUnmarshalUBD(cdc, kv.Key, kv.Value) From e4b5e2c9f3e23376b33d38c8db93e74292dc2281 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Sun, 28 Oct 2018 21:37:19 -0700 Subject: [PATCH 02/61] Make simulation use a transition matrix for block size This enables simulating periods of high load, and periods of low to no load. (low load because future ops will still terminate in that time frame) --- PENDING.md | 1 + client/lcd/certificates.go | 2 +- x/mock/simulation/constants.go | 6 +++ x/mock/simulation/random_simulate_blocks.go | 41 ++++++++++++--------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/PENDING.md b/PENDING.md index aa7cca2c4a..a43485bebd 100644 --- a/PENDING.md +++ b/PENDING.md @@ -37,6 +37,7 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - \#1924 [simulation] Use a transition matrix for block size * Tendermint diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go index 1516ed35af..f47f2397c7 100644 --- a/client/lcd/certificates.go +++ b/client/lcd/certificates.go @@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, - IsCA: true, + IsCA: true, } hosts := strings.Split(host, ",") for _, h := range hosts { diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go index a96d4541f1..2c4543b72d 100644 --- a/x/mock/simulation/constants.go +++ b/x/mock/simulation/constants.go @@ -28,4 +28,10 @@ var ( {10, 50, 5}, {0, 10, 1000}, }) + // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 + blockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ + {85, 5, 0}, + {15, 92, 1}, + {0, 3, 99}, + }) ) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 2506af377b..78939698b6 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -108,7 +108,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, blockLogBuilders = make([]*strings.Builder, numBlocks) } displayLogs := logPrinter(testingMode, blockLogBuilders) - blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, displayLogs) + blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) if !testingMode { b.ResetTimer() } else { @@ -142,7 +142,6 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } ctx := app.NewContext(false, header) - thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small logWriter("Queued operations") @@ -153,9 +152,8 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, assertAllInvariants(t, app, header, invariants, "QueuedOperations", displayLogs) } - thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan logWriter("Standard operations") - operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) + operations := blockSimulator(r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan if testingMode { // Make sure invariants hold at end of block @@ -200,9 +198,14 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // 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, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func( - blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { - totalOpWeight := 0 +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + var ( + lastBlocksizeState = 0 // state for [4 * uniform distribution] + totalOpWeight = 0 + blocksize int + ) + for i := 0; i < len(ops); i++ { totalOpWeight += ops[i].Weight } @@ -217,8 +220,10 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f // shouldn't happen return ops[0].Op } - return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + lastBlocksizeState, blocksize = getBlockSize(r, lastBlocksizeState, avgBlockSize) for j := 0; j < blocksize; j++ { logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) if err != nil { @@ -253,16 +258,18 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B return } -func getBlockSize(r *rand.Rand, blockSize int) int { - load := r.Float64() - switch { - case load < 0.33: - return 0 - case load < 0.66: - return r.Intn(blockSize * 2) - default: - return r.Intn(blockSize * 4) +func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { + // TODO: Make blockSizeTransitionMatrix non-global + // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize + state = 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. From 100f88800b02f79e04d1d6e4a47471077a6357eb Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Mon, 29 Oct 2018 09:37:42 -0700 Subject: [PATCH 03/61] address bez's comments --- x/mock/simulation/random_simulate_blocks.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 78939698b6..7c3c38240d 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -198,8 +198,13 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // 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, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, + event func(string), invariants []Invariant, + ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, + totalNumBlocks int, avgBlockSize int, displayLogs func()) func( + r *rand.Rand, + app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { var ( lastBlocksizeState = 0 // state for [4 * uniform distribution] totalOpWeight = 0 @@ -258,6 +263,12 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *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, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { // TODO: Make blockSizeTransitionMatrix non-global // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize From ba7ed9d7cbfe3795281631ae3335c0d6026ab7b5 Mon Sep 17 00:00:00 2001 From: zramsay Date: Mon, 29 Oct 2018 13:42:44 -0400 Subject: [PATCH 04/61] fix flags in docs, closes #2530 --- docs/sdk/clients.md | 8 ++++---- docs/validators/validator-setup.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index f784dd238d..01deeae417 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -286,8 +286,8 @@ A redelegation is a type delegation that allows you to bond illiquid tokens from ```bash gaiacli tx redelegate begin \ - --address-validator-source= \ - --address-validator-dest= \ + --addr-validator-source= \ + --addr-validator-dest= \ --shares-percent=50 \ --from= \ --chain-id= @@ -311,8 +311,8 @@ Once you begin an redelegation, you can see it's information by using the follow ```bash gaiacli query redelegation \ --address-delegator= \ - --address-validator-source= \ - --address-validator-dest= \ + --addr-validator-source= \ + --addr-validator-dest= \ ``` Or if you want to check all your current unbonding-delegations with disctinct validators: diff --git a/docs/validators/validator-setup.md b/docs/validators/validator-setup.md index 43a3b06728..217a0d341c 100644 --- a/docs/validators/validator-setup.md +++ b/docs/validators/validator-setup.md @@ -32,7 +32,7 @@ Don't use more `steak` thank you have! You can always get more by using the [Fau gaiacli tx create-validator \ --amount=5steak \ --pubkey=$(gaiad tendermint show-validator) \ - --address-validator= + --from= --moniker="choose a moniker" \ --chain-id= \ --name= \ From c93b116d2371a8ae19204b32e6a4d0af01bf3cf3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Oct 2018 00:05:55 +0100 Subject: [PATCH 05/61] Merge PR #2616: Block redelegations to the same validator --- PENDING.md | 1 + x/stake/keeper/delegation.go | 4 ++++ x/stake/keeper/delegation_test.go | 26 ++++++++++++++++++++++++++ x/stake/types/errors.go | 4 ++++ 4 files changed, 35 insertions(+) diff --git a/PENDING.md b/PENDING.md index aa7cca2c4a..02d136bbce 100644 --- a/PENDING.md +++ b/PENDING.md @@ -37,6 +37,7 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - #2610 [x/stake] Block redelegation to and from the same validator * Tendermint diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index a5d08d4892..97acc24042 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -541,6 +541,10 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) { + if bytes.Equal(valSrcAddr, valDstAddr) { + return types.Redelegation{}, types.ErrSelfRedelegation(k.Codespace()) + } + // check if there is already a redelgation in progress from src to dst // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 24476ca3cc..fcf2f42067 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -581,6 +581,32 @@ func TestRedelegation(t *testing.T) { require.Equal(t, 0, len(redelegations)) } +func TestRedelegateToSameValidator(t *testing.T) { + + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = TestingUpdateValidator(keeper, ctx, validator) + pool = keeper.GetPool(ctx) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.Delegation{ + DelegatorAddr: val0AccAddr, + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) + require.Error(t, err) + +} + func TestRedelegateSelfDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index e8c85800fe..d76eb39c15 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -155,6 +155,10 @@ func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found") } +func ErrSelfRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "cannot redelegate to the same validator") +} + func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found") } From 0d8325b859e67fac8476d0c74c4f68afb4a9b752 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 29 Oct 2018 16:10:39 -0700 Subject: [PATCH 06/61] Merge PR #2623: Speedup simulator by switching to goleveldb Due to requiring app.Commit() at the moment, golevel db is significantly faster than a memdb --- Makefile | 6 +++--- cmd/gaia/app/sim_test.go | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index b36ef61d4d..9d4ad0a255 100644 --- a/Makefile +++ b/Makefile @@ -169,13 +169,13 @@ test_sim_gaia_nondeterminism: test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" - @bash scripts/multisim.sh 10 + @bash scripts/multisim.sh 25 -SIM_NUM_BLOCKS ?= 210 +SIM_NUM_BLOCKS ?= 500 SIM_BLOCK_SIZE ?= 200 SIM_COMMIT ?= true test_sim_gaia_benchmark: diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 384afbf6db..9f94a82f7c 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "io/ioutil" "math/rand" "os" "testing" @@ -141,7 +142,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { var logger log.Logger logger = log.NewNopLogger() var db dbm.DB - dir := os.TempDir() + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") db, _ = dbm.NewGoLevelDB("Simulation", dir) defer func() { db.Close() @@ -183,7 +184,13 @@ func TestFullGaiaSimulation(t *testing.T) { } else { logger = log.NewNopLogger() } - db := dbm.NewMemDB() + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") + db, _ = dbm.NewGoLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() app := NewGaiaApp(logger, db, nil) require.Equal(t, "GaiaApp", app.Name()) @@ -198,7 +205,11 @@ func TestFullGaiaSimulation(t *testing.T) { commit, ) if commit { - fmt.Println("Database Size", db.Stats()["database.size"]) + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("GoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) } require.Nil(t, err) } From 206e51079c613e5448596dd3c10c7993bcc0a588 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Tue, 30 Oct 2018 20:43:16 -0700 Subject: [PATCH 07/61] fix block offsets in printing simulation block number --- x/mock/simulation/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index c408c5328c..e82c1bf5eb 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -146,7 +146,7 @@ func logPrinter(testingmode bool, logs []*strings.Builder) func() { } for i := 0; i < numLoggers; i++ { if f != nil { - _, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i)) + _, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i+1)) if err != nil { panic("Failed to write logs to file") } @@ -155,7 +155,7 @@ func logPrinter(testingmode bool, logs []*strings.Builder) func() { panic("Failed to write logs to file") } } else { - fmt.Printf("Begin block %d\n", i) + fmt.Printf("Begin block %d\n", i+1) fmt.Println((*logs[i]).String()) } } From 6316e1241e5132728d993a238059ff170a34ae16 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 31 Oct 2018 04:04:46 -0700 Subject: [PATCH 08/61] Merge PR #2644: Simulation: Print last block when there is an error There was an off by one error in the log printing function previously --- x/mock/simulation/random_simulate_blocks.go | 2 +- x/mock/simulation/util.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 2506af377b..bbe27bf6c7 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -221,11 +221,11 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { 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) } - logWriter(logUpdate) queueOperations(operationQueue, timeOperationQueue, futureOps) if testingMode { diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index e82c1bf5eb..84a4ad2a33 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -134,7 +134,7 @@ func logPrinter(testingmode bool, logs []*strings.Builder) func() { for i := 0; i < len(logs); i++ { // We're passed the last created block if logs[i] == nil { - numLoggers = i - 1 + numLoggers = i break } } From 89047d8ebd7a06a4a538bc3817a858fbd7e71d2d Mon Sep 17 00:00:00 2001 From: Rigel Date: Wed, 31 Oct 2018 07:07:06 -0400 Subject: [PATCH 09/61] Merge PR #2642: Add todo diagrams --- docs/PRIORITIES.md | 2 ++ docs/resources/diagrams/todo.md | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 docs/resources/diagrams/todo.md diff --git a/docs/PRIORITIES.md b/docs/PRIORITIES.md index 02aa495bbb..522f1b8660 100644 --- a/docs/PRIORITIES.md +++ b/docs/PRIORITIES.md @@ -36,6 +36,8 @@ - Tags [#1780](https://github.com/cosmos/cosmos-sdk/issues/1780) # Lower priority +- Create some diagrams (see `docs/resources/diagrams/todo.md`) + ## Governance v2 - Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926 diff --git a/docs/resources/diagrams/todo.md b/docs/resources/diagrams/todo.md new file mode 100644 index 0000000000..b032b55cf1 --- /dev/null +++ b/docs/resources/diagrams/todo.md @@ -0,0 +1,17 @@ +The following diagrams should be created to aid in comprehension of the SDK: + - Genesis circuit + - App structure (aka use of baseapp in something like gaia) + - Simulation framework + - Slashing Mechanism + - Staking Mechanism + - Staking/Slashing Mechanism specific to use of hooks + - Governance Mechanism + - Distribution Mechanism + - Inflation Mechanism (easier) + - IBC Mechanism + +These diagrams should reference specific structs/interfaces from the codebase, +logic flow and interconnectivity with other mechanisms etc. It's recommended that +https://www.draw.io/ be used, hence the raw diagram xml can be saved directly to +the Cosmos-SDK repo and adapted with the codebase. + From bc583ab1d0b9d02c6b3935f16f48f9fb58e3e2f0 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 15:37:20 -0700 Subject: [PATCH 10/61] Various sign command improvements - Exit with error if the user is attempting to sign with a key whose address is not among those who are expected to sign the transaction. - Add --print-signature-only to output only the generated signature. --- client/utils/utils.go | 3 ++- x/auth/client/cli/sign.go | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/client/utils/utils.go b/client/utils/utils.go index f5cf04168c..9d20f0ab88 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -123,7 +123,8 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, // Check whether the address is a signer if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { - fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'\n", name) + return signedStdTx, fmt.Errorf( + "The generated transaction's intended signer does not match the given signer: %q", name) } if !offline && txBldr.AccountNumber == 0 { diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 67c64dc2ad..fdc7269ce5 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -20,6 +20,7 @@ const ( flagAppend = "append" flagPrintSigs = "print-sigs" flagOffline = "offline" + flagSigOnly = "print-signature-only" ) // GetSignCommand returns the sign command @@ -38,6 +39,7 @@ recommended to set such parameters manually.`, } cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") + cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.") cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") return cmd @@ -59,14 +61,24 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) txBldr := authtxb.NewTxBuilderFromCLI() - newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, viper.GetBool(flagAppend), viper.GetBool(flagOffline)) + // if --print-signature-only is on, then override --append + generateSignatureOnly := viper.GetBool(flagSigOnly) + append := viper.GetBool(flagAppend) && !generateSignatureOnly + newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, append, viper.GetBool(flagOffline)) if err != nil { return err } + var json []byte - if cliCtx.Indent { + + switch { + case generateSignatureOnly && cliCtx.Indent: + json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") + case generateSignatureOnly && !cliCtx.Indent: + json, err = cdc.MarshalJSON(newTx.Signatures[0]) + case !generateSignatureOnly && cliCtx.Indent: json, err = cdc.MarshalJSONIndent(newTx, "", " ") - } else { + case !generateSignatureOnly && !cliCtx.Indent: json, err = cdc.MarshalJSON(newTx) } if err != nil { From 933592d05222f79dbb523621d2ba86bdd3f95549 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 15:52:19 -0700 Subject: [PATCH 11/61] Check sanity of signatures and report errors when run with --print-sigs --- x/auth/client/cli/sign.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index fdc7269ce5..4b04139f21 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -91,13 +91,19 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. func printSignatures(stdTx auth.StdTx) { fmt.Println("Signers:") - for i, signer := range stdTx.GetSigners() { + signers := stdTx.GetSigners() + for i, signer := range signers { fmt.Printf(" %v: %v\n", i, signer.String()) } fmt.Println("") fmt.Println("Signatures:") for i, sig := range stdTx.GetSignatures() { - fmt.Printf(" %v: %v\n", i, sdk.AccAddress(sig.Address()).String()) + sigAddr := sdk.AccAddress(sig.Address()) + sigSanity := "OK" + if i >= len(signers) || !sigAddr.Equals(signers[i]) { + sigSanity = "ERROR" + } + fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity) } return } From 4bf17e82b4539f2f2ea70687bb8d3cfb7b1af27c Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 16:11:13 -0700 Subject: [PATCH 12/61] Improve errors reporting --- x/auth/client/cli/sign.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 4b04139f21..98c3e750ce 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -2,9 +2,8 @@ package cli import ( "fmt" - "io/ioutil" - "github.com/spf13/viper" + "io/ioutil" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -13,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" + "github.com/tendermint/go-amino" ) const ( @@ -53,7 +52,9 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. } if viper.GetBool(flagPrintSigs) { - printSignatures(stdTx) + if !printSignatures(stdTx) { + return fmt.Errorf("signatures validation failed") + } return nil } @@ -89,23 +90,31 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. } } -func printSignatures(stdTx auth.StdTx) { +func printSignatures(stdTx auth.StdTx) bool { fmt.Println("Signers:") signers := stdTx.GetSigners() for i, signer := range signers { fmt.Printf(" %v: %v\n", i, signer.String()) } + + sigs := stdTx.GetSignatures() fmt.Println("") fmt.Println("Signatures:") + success := true + if len(sigs) != len(signers) { + success = false + } for i, sig := range stdTx.GetSignatures() { sigAddr := sdk.AccAddress(sig.Address()) sigSanity := "OK" if i >= len(signers) || !sigAddr.Equals(signers[i]) { - sigSanity = "ERROR" + sigSanity = fmt.Sprintf("ERROR: signature %d does not match its respective signer", i) + if success { success = false } } fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity) } - return + fmt.Println("") + return success } func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) { From 62974e627eb7385e94a115888f45b0e3b88c6fc4 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 16:52:54 -0700 Subject: [PATCH 13/61] Improve online docs --- x/auth/client/cli/sign.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 98c3e750ce..7d17dde44b 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -30,6 +30,9 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm Long: `Sign transactions created with the --generate-only flag. Read a transaction from , sign it, and print its JSON encoding. +If the flag --print-signature-only flag is on, it outputs a JSON representation +of the generated signature only. + The --offline flag makes sure that the client will not reach out to the local cache. Thus account number or sequence number lookups will not be performed and it is recommended to set such parameters manually.`, From a4c4e280f8d0d53522ece2b3f14e67fb2e715acf Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 31 Oct 2018 13:42:38 +0000 Subject: [PATCH 14/61] Refresh PENDING.md --- PENDING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 4453ddf8e2..dd34ed56d0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,7 +18,8 @@ FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [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) Improve --print-sigs mode. It now performs a complete set of sanity checks and reports to the user. Also added --raw-signature to print the signature only, not the whole transaction. * Gaia From 69a7c06ef7830cbab492e6903ef4722d7ad47738 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 16:59:19 -0700 Subject: [PATCH 15/61] Find better name for --print-signature-only --- x/auth/client/cli/sign.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 7d17dde44b..ba8372e982 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -16,10 +16,10 @@ import ( ) const ( - flagAppend = "append" - flagPrintSigs = "print-sigs" - flagOffline = "offline" - flagSigOnly = "print-signature-only" + flagAppend = "append" + flagPrintSigs = "print-sigs" + flagOffline = "offline" + flagRawSignature = "raw-signature" ) // GetSignCommand returns the sign command @@ -30,7 +30,7 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm Long: `Sign transactions created with the --generate-only flag. Read a transaction from , sign it, and print its JSON encoding. -If the flag --print-signature-only flag is on, it outputs a JSON representation +If the flag --raw-signature flag is on, it outputs a JSON representation of the generated signature only. The --offline flag makes sure that the client will not reach out to the local cache. @@ -41,7 +41,7 @@ recommended to set such parameters manually.`, } cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") - cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.") + cmd.Flags().Bool(flagRawSignature, false, "Print only the generated signature, then exit.") cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") return cmd @@ -66,7 +66,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. txBldr := authtxb.NewTxBuilderFromCLI() // if --print-signature-only is on, then override --append - generateSignatureOnly := viper.GetBool(flagSigOnly) + generateSignatureOnly := viper.GetBool(flagRawSignature) append := viper.GetBool(flagAppend) && !generateSignatureOnly newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, append, viper.GetBool(flagOffline)) if err != nil { @@ -112,7 +112,9 @@ func printSignatures(stdTx auth.StdTx) bool { sigSanity := "OK" if i >= len(signers) || !sigAddr.Equals(signers[i]) { sigSanity = fmt.Sprintf("ERROR: signature %d does not match its respective signer", i) - if success { success = false } + if success { + success = false + } } fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity) } From e65c02dd42d9d5e73a8d276aef25b7d108f748c4 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 17:37:28 -0700 Subject: [PATCH 16/61] Fix integration tests --- cmd/gaia/cli_test/cli_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index ad1549641b..d965a894d6 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -493,7 +493,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test sign --print-sigs success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( "gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name())) - require.True(t, success) + require.False(t, success) require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout) // Test sign From 31c062689de71e0ad4499e7b1cf85e43f51962e0 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 17:51:46 -0700 Subject: [PATCH 17/61] Validate --name --- x/auth/client/cli/sign.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index ba8372e982..7009bd9689 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "github.com/pkg/errors" "github.com/spf13/viper" "io/ioutil" @@ -62,6 +63,9 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. } name := viper.GetString(client.FlagName) + if name == "" { + return errors.New("required flag \"name\" has not been set") + } cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) txBldr := authtxb.NewTxBuilderFromCLI() From a5142951286d0eb0aee7a3afda11cc9201697785 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 22 Oct 2018 17:53:02 -0700 Subject: [PATCH 18/61] Fix integration tests --- cmd/gaia/cli_test/cli_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index d965a894d6..ba3839779b 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -494,7 +494,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( "gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name())) require.False(t, success) - require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout) // Test sign success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( @@ -513,7 +513,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( "gaiacli tx sign %v --print-sigs %v", flags, signedTxFile.Name())) require.True(t, success) - require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\n", fooAddr.String(), fooAddr.String()), stdout) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(), + fooAddr.String()), stdout) // Test broadcast fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) From 53f1233f0a16377e3ce0f2151c5ca8945090cf67 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 23 Oct 2018 11:53:47 -0700 Subject: [PATCH 19/61] s/--print-sigs/--validate-signatures/ --- cmd/gaia/cli_test/cli_test.go | 12 +++++++----- x/auth/client/cli/sign.go | 14 ++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index ba3839779b..b4791bf126 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -445,7 +445,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) // start gaiad server - proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) + proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf( + "gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) defer proc.Stop(false) tests.WaitForTMStart(port) @@ -490,9 +491,9 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { unsignedTxFile := writeToNewTempFile(t, stdout) defer os.Remove(unsignedTxFile.Name()) - // Test sign --print-sigs + // Test sign --validate-signatures success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name())) + "gaiacli tx sign %v --validate-signatures %v", flags, unsignedTxFile.Name())) require.False(t, success) require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout) @@ -511,7 +512,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test sign --print-signatures success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli tx sign %v --print-sigs %v", flags, signedTxFile.Name())) + "gaiacli tx sign %v --validate-signatures %v", flags, signedTxFile.Name())) require.True(t, success) require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(), fooAddr.String()), stdout) @@ -520,7 +521,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) - success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) + success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( + "gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) require.True(t, success) var result struct { Response abci.ResponseDeliverTx diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 7009bd9689..bc4ad59ab3 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -18,9 +18,9 @@ import ( const ( flagAppend = "append" - flagPrintSigs = "print-sigs" + flagPrintSigs = "validate-signatures" flagOffline = "offline" - flagRawSignature = "raw-signature" + flagRawSignature = "sig-only" ) // GetSignCommand returns the sign command @@ -31,7 +31,7 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm Long: `Sign transactions created with the --generate-only flag. Read a transaction from , sign it, and print its JSON encoding. -If the flag --raw-signature flag is on, it outputs a JSON representation +If the flag --sig-only flag is on, it outputs a JSON representation of the generated signature only. The --offline flag makes sure that the client will not reach out to the local cache. @@ -41,9 +41,11 @@ recommended to set such parameters manually.`, Args: cobra.ExactArgs(1), } cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") - cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") + cmd.Flags().Bool(flagAppend, true, + "Append the signature to the existing ones. If disabled, old signatures would be overwritten") cmd.Flags().Bool(flagRawSignature, false, "Print only the generated signature, then exit.") - cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") + cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction, "+ + "those who have already signed it, and make sure that signatures are in the correct order.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") return cmd } @@ -69,7 +71,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) txBldr := authtxb.NewTxBuilderFromCLI() - // if --print-signature-only is on, then override --append + // if --sig-only is on, then override --append generateSignatureOnly := viper.GetBool(flagRawSignature) append := viper.GetBool(flagAppend) && !generateSignatureOnly newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, append, viper.GetBool(flagOffline)) From f752525d5496b4011da6213ccd874003a4f36db3 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 23 Oct 2018 12:41:24 -0700 Subject: [PATCH 20/61] s/--sig-only/--signature-only/ --- x/auth/client/cli/sign.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index bc4ad59ab3..60deefb3dc 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -20,7 +20,7 @@ const ( flagAppend = "append" flagPrintSigs = "validate-signatures" flagOffline = "offline" - flagRawSignature = "sig-only" + flagRawSignature = "signature-only" ) // GetSignCommand returns the sign command @@ -31,7 +31,7 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm Long: `Sign transactions created with the --generate-only flag. Read a transaction from , sign it, and print its JSON encoding. -If the flag --sig-only flag is on, it outputs a JSON representation +If the flag --signature-only flag is on, it outputs a JSON representation of the generated signature only. The --offline flag makes sure that the client will not reach out to the local cache. @@ -71,7 +71,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) txBldr := authtxb.NewTxBuilderFromCLI() - // if --sig-only is on, then override --append + // if --signature-only is on, then override --append generateSignatureOnly := viper.GetBool(flagRawSignature) append := viper.GetBool(flagAppend) && !generateSignatureOnly newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, append, viper.GetBool(flagOffline)) From bb734c43c6ffa3819269e77125d3ca718ecc056e Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 25 Oct 2018 04:44:42 +0100 Subject: [PATCH 21/61] Docs updated --- docs/sdk/clients.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 73d9a3b9a3..4f0669183f 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -181,6 +181,12 @@ gaiacli tx sign \ unsignedSendTx.json > signedSendTx.json ``` +You can validate the transaction's signagures by typing the following: + +```bash +gaiacli tx sign --validate-signatures signedSendTx.json +``` + You can broadcast the signed transaction to a node by providing the JSON file to the following command: ``` From 17f799abb08d352e618c8914a13a6a3b6a39a584 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 31 Oct 2018 13:43:17 +0000 Subject: [PATCH 22/61] Update PENDING.md --- PENDING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index dd34ed56d0..e9f5022101 100644 --- a/PENDING.md +++ b/PENDING.md @@ -19,7 +19,8 @@ FEATURES * Gaia CLI (`gaiacli`) * [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) Improve --print-sigs mode. It now performs a complete set of sanity checks and reports to the user. Also added --raw-signature to print the signature only, not the whole transaction. + * [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] [\#2554](https://github.com/cosmos/cosmos-sdk/issues/2554) Make `gaiacli keys show` multisig ready. * Gaia From 9e397907be91bcc9a759890366941379d7ce557f Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 29 Oct 2018 15:11:32 +0000 Subject: [PATCH 23/61] Rename append, it's go builtin --- x/auth/client/cli/sign.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 60deefb3dc..812c2be248 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -73,8 +73,8 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. // if --signature-only is on, then override --append generateSignatureOnly := viper.GetBool(flagRawSignature) - append := viper.GetBool(flagAppend) && !generateSignatureOnly - newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, append, viper.GetBool(flagOffline)) + appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly + newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, appendSig, viper.GetBool(flagOffline)) if err != nil { return err } From 44d2347109d49d3e9d99598bdab4bf753b493bd7 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 29 Oct 2018 15:13:36 +0000 Subject: [PATCH 24/61] Set success = false when it fails --- x/auth/client/cli/sign.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 812c2be248..fd8d397965 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -118,9 +118,7 @@ func printSignatures(stdTx auth.StdTx) bool { sigSanity := "OK" if i >= len(signers) || !sigAddr.Equals(signers[i]) { sigSanity = fmt.Sprintf("ERROR: signature %d does not match its respective signer", i) - if success { - success = false - } + success = false } fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity) } From 84aef0d106c566d378d0d40bf0766b752ed935a0 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 29 Oct 2018 15:15:59 +0000 Subject: [PATCH 25/61] Apply suggestions from bez --- x/auth/client/cli/sign.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index fd8d397965..4ea7343557 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -18,9 +18,9 @@ import ( const ( flagAppend = "append" - flagPrintSigs = "validate-signatures" + flagValidateSigs = "validate-signatures" flagOffline = "offline" - flagRawSignature = "signature-only" + flagSigOnly = "signature-only" ) // GetSignCommand returns the sign command @@ -43,8 +43,8 @@ recommended to set such parameters manually.`, cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") - cmd.Flags().Bool(flagRawSignature, false, "Print only the generated signature, then exit.") - cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction, "+ + cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.") + cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+ "those who have already signed it, and make sure that signatures are in the correct order.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") return cmd @@ -57,7 +57,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. return } - if viper.GetBool(flagPrintSigs) { + if viper.GetBool(flagValidateSigs) { if !printSignatures(stdTx) { return fmt.Errorf("signatures validation failed") } @@ -72,7 +72,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. txBldr := authtxb.NewTxBuilderFromCLI() // if --signature-only is on, then override --append - generateSignatureOnly := viper.GetBool(flagRawSignature) + generateSignatureOnly := viper.GetBool(flagSigOnly) appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, appendSig, viper.GetBool(flagOffline)) if err != nil { From 6c8791f5055950bcae4b2554019b44f48d987680 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 29 Oct 2018 15:34:11 +0000 Subject: [PATCH 26/61] Nest switches --- x/auth/client/cli/sign.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 4ea7343557..cb128e38c0 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -81,15 +81,21 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. var json []byte - switch { - case generateSignatureOnly && cliCtx.Indent: - json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") - case generateSignatureOnly && !cliCtx.Indent: - json, err = cdc.MarshalJSON(newTx.Signatures[0]) - case !generateSignatureOnly && cliCtx.Indent: - json, err = cdc.MarshalJSONIndent(newTx, "", " ") - case !generateSignatureOnly && !cliCtx.Indent: - json, err = cdc.MarshalJSON(newTx) + switch generateSignatureOnly { + case true: + switch cliCtx.Indent { + case true: + json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") + default: + json, err = cdc.MarshalJSON(newTx.Signatures[0]) + } + default: + switch cliCtx.Indent { + case true: + json, err = cdc.MarshalJSONIndent(newTx, "", " ") + default: + json, err = cdc.MarshalJSON(newTx) + } } if err != nil { return err From 06989bee618965d33ed1e443c3fadabb545e2e41 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 31 Oct 2018 13:44:00 +0000 Subject: [PATCH 27/61] Fix rebase --- PENDING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PENDING.md b/PENDING.md index e9f5022101..a376a154c3 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,9 +18,10 @@ FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [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] [\#2554](https://github.com/cosmos/cosmos-sdk/issues/2554) Make `gaiacli keys show` multisig ready. + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. + * [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction. * Gaia From dbbf0647f0dd8057eb1ae593cfcf66d6b4f7e4a5 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 31 Oct 2018 13:50:01 +0000 Subject: [PATCH 28/61] Document what --validate-signatures does --- x/auth/client/cli/sign.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index cb128e38c0..f4a4548d41 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -34,6 +34,10 @@ Read a transaction from , sign it, and print its JSON encoding. If the flag --signature-only flag is on, it outputs a JSON representation of the generated signature only. +If the flag --validate-signatures is on, then the command would check whether all required +signers have signed the transactions and whether the signatures were collected in the right +order. + The --offline flag makes sure that the client will not reach out to the local cache. Thus account number or sequence number lookups will not be performed and it is recommended to set such parameters manually.`, From 5e2efd999aae9ed12352ee4e64ce3f498ec6a811 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 31 Oct 2018 17:37:50 +0100 Subject: [PATCH 29/61] perform minor doc and function cleanup --- x/mock/simulation/random_simulate_blocks.go | 27 ++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 7c3c38240d..ba477864f2 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -22,6 +22,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +type blockSimFn func( + r *rand.Rand, + app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string), +) (opCount int) + // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, @@ -201,10 +207,8 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, - totalNumBlocks int, avgBlockSize int, displayLogs func()) func( - r *rand.Rand, - app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { + var ( lastBlocksizeState = 0 // state for [4 * uniform distribution] totalOpWeight = 0 @@ -263,15 +267,16 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *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. */ +// 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, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { // TODO: Make blockSizeTransitionMatrix non-global - // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize + // TODO: Make default blocksize transition matrix actually make the average + // blocksize equal to avgBlockSize. state = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState) if state == 0 { blocksize = r.Intn(avgBlockSize * 4) From 835315dcaa6745bc28a258131205adfadbf7a691 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Wed, 31 Oct 2018 10:11:41 -0700 Subject: [PATCH 30/61] move typedef --- x/mock/simulation/random_simulate_blocks.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index ba477864f2..098c1647ab 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -22,12 +22,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -type blockSimFn func( - r *rand.Rand, - app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string), -) (opCount int) - // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, @@ -202,6 +196,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, 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, From 9cf53f25f7bfff9454aa2728e05d8eccbb386aa6 Mon Sep 17 00:00:00 2001 From: svaishnavy Date: Wed, 31 Oct 2018 20:13:13 +0100 Subject: [PATCH 31/61] Merge PR #2614: Configurable Bech32 prefix for SDK users --- PENDING.md | 1 + cmd/gaia/cmd/gaiacli/main.go | 7 ++ cmd/gaia/cmd/gaiad/main.go | 8 ++ cmd/gaia/cmd/gaiadebug/main.go | 7 ++ examples/democoin/cmd/democli/main.go | 9 ++ examples/democoin/cmd/democoind/main.go | 9 ++ types/address.go | 36 +++++--- types/address_test.go | 43 ++++++++++ types/config.go | 105 ++++++++++++++++++++++++ 9 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 types/config.go diff --git a/PENDING.md b/PENDING.md index ff6196848b..8a16136c09 100644 --- a/PENDING.md +++ b/PENDING.md @@ -26,6 +26,7 @@ FEATURES * Gaia * SDK + * (#1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 6f11e68a5a..212a670214 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -15,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -47,6 +48,12 @@ func main() { cobra.EnableCommandSorting = false cdc := app.MakeCodec() + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 1304b32f34..ed777bf478 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -18,10 +18,18 @@ import ( "github.com/cosmos/cosmos-sdk/cmd/gaia/app" gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" ) func main() { cdc := app.MakeCodec() + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + ctx := server.NewDefaultContext() cobra.EnableCommandSorting = false rootCmd := &cobra.Command{ diff --git a/cmd/gaia/cmd/gaiadebug/main.go b/cmd/gaia/cmd/gaiadebug/main.go index 73840537b2..96887376cd 100644 --- a/cmd/gaia/cmd/gaiadebug/main.go +++ b/cmd/gaia/cmd/gaiadebug/main.go @@ -19,6 +19,13 @@ import ( ) func init() { + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + rootCmd.AddCommand(txCmd) rootCmd.AddCommand(pubkeyCmd) rootCmd.AddCommand(addrCmd) diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 08f1311687..6bb2da8680 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -21,6 +21,8 @@ import ( coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli" powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli" simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // rootCmd is the entry point for this binary @@ -38,6 +40,13 @@ func main() { // get the codec cdc := app.MakeCodec() + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("demoacc", "demopub") + config.SetBech32PrefixForValidator("demoval", "demovalpub") + config.SetBech32PrefixForConsensusNode("democons", "democonspub") + config.Seal() + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 692564b88e..b0256b1749 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -23,6 +23,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -135,6 +136,14 @@ func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (j func main() { cdc := app.MakeCodec() + + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("demoacc", "demopub") + config.SetBech32PrefixForValidator("demoval", "demovalpub") + config.SetBech32PrefixForConsensusNode("democons", "democonspub") + config.Seal() + ctx := server.NewDefaultContext() rootCmd := &cobra.Command{ diff --git a/types/address.go b/types/address.go index ae13b2ad0a..36eb5ac32b 100644 --- a/types/address.go +++ b/types/address.go @@ -55,7 +55,8 @@ func AccAddressFromHex(address string) (addr AccAddress, err error) { // AccAddressFromBech32 creates an AccAddress from a Bech32 string. func AccAddressFromBech32(address string) (addr AccAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixAccAddr) + bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixAccAddr) if err != nil { return nil, err } @@ -124,7 +125,8 @@ func (aa AccAddress) Bytes() []byte { // String implements the Stringer interface. func (aa AccAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixAccAddr, aa.Bytes()) + bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixAccAddr, aa.Bytes()) if err != nil { panic(err) } @@ -169,7 +171,8 @@ func ValAddressFromHex(address string) (addr ValAddress, err error) { // ValAddressFromBech32 creates a ValAddress from a Bech32 string. func ValAddressFromBech32(address string) (addr ValAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixValAddr) + bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixValAddr) if err != nil { return nil, err } @@ -239,7 +242,8 @@ func (va ValAddress) Bytes() []byte { // String implements the Stringer interface. func (va ValAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixValAddr, va.Bytes()) + bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixValAddr, va.Bytes()) if err != nil { panic(err) } @@ -284,7 +288,8 @@ func ConsAddressFromHex(address string) (addr ConsAddress, err error) { // ConsAddressFromBech32 creates a ConsAddress from a Bech32 string. func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixConsAddr) + bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixConsAddr) if err != nil { return nil, err } @@ -359,7 +364,8 @@ func (ca ConsAddress) Bytes() []byte { // String implements the Stringer interface. func (ca ConsAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixConsAddr, ca.Bytes()) + bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixConsAddr, ca.Bytes()) if err != nil { panic(err) } @@ -387,7 +393,8 @@ func (ca ConsAddress) Format(s fmt.State, verb rune) { // Bech32ifyAccPub returns a Bech32 encoded string containing the // Bech32PrefixAccPub prefix for a given account PubKey. func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) + bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixAccPub, pub.Bytes()) } // MustBech32ifyAccPub returns the result of Bech32ifyAccPub panicing on failure. @@ -403,7 +410,8 @@ func MustBech32ifyAccPub(pub crypto.PubKey) string { // Bech32ifyValPub returns a Bech32 encoded string containing the // Bech32PrefixValPub prefix for a given validator operator's PubKey. func Bech32ifyValPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes()) + bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixValPub, pub.Bytes()) } // MustBech32ifyValPub returns the result of Bech32ifyValPub panicing on failure. @@ -419,7 +427,8 @@ func MustBech32ifyValPub(pub crypto.PubKey) string { // Bech32ifyConsPub returns a Bech32 encoded string containing the // Bech32PrefixConsPub prefixfor a given consensus node's PubKey. func Bech32ifyConsPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixConsPub, pub.Bytes()) + bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixConsPub, pub.Bytes()) } // MustBech32ifyConsPub returns the result of Bech32ifyConsPub panicing on @@ -436,7 +445,8 @@ func MustBech32ifyConsPub(pub crypto.PubKey) string { // GetAccPubKeyBech32 creates a PubKey for an account with a given public key // string using the Bech32 Bech32PrefixAccPub prefix. func GetAccPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixAccPub) + bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixAccPub) if err != nil { return nil, err } @@ -463,7 +473,8 @@ func MustGetAccPubKeyBech32(pubkey string) (pk crypto.PubKey) { // GetValPubKeyBech32 creates a PubKey for a validator's operator with a given // public key string using the Bech32 Bech32PrefixValPub prefix. func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixValPub) + bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixValPub) if err != nil { return nil, err } @@ -490,7 +501,8 @@ func MustGetValPubKeyBech32(pubkey string) (pk crypto.PubKey) { // GetConsPubKeyBech32 creates a PubKey for a consensus node with a given public // key string using the Bech32 Bech32PrefixConsPub prefix. func GetConsPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixConsPub) + bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixConsPub) if err != nil { return nil, err } diff --git a/types/address_test.go b/types/address_test.go index e2ec36876c..51c44a12cd 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -9,6 +9,8 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" + "strings" + "github.com/cosmos/cosmos-sdk/types" ) @@ -178,3 +180,44 @@ func TestConsAddress(t *testing.T) { require.NotNil(t, err) } } + +const letterBytes = "abcdefghijklmnopqrstuvwxyz" + +func RandString(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) +} + +func TestConfiguredPrefix(t *testing.T) { + var pub ed25519.PubKeyEd25519 + for length := 1; length < 10; length++ { + for times := 1; times < 20; times++ { + rand.Read(pub[:]) + // Test if randomly generated prefix of a given length works + prefix := RandString(length) + // Assuming that GetConfig is not sealed. + config := types.GetConfig() + config.SetBech32PrefixForAccount(prefix+"acc", prefix+"pub") + acc := types.AccAddress(pub.Address()) + require.True(t, strings.HasPrefix(acc.String(), prefix+"acc")) + bech32Pub := types.MustBech32ifyAccPub(pub) + require.True(t, strings.HasPrefix(bech32Pub, prefix+"pub")) + + config.SetBech32PrefixForValidator(prefix+"valaddr", prefix+"valpub") + val := types.ValAddress(pub.Address()) + require.True(t, strings.HasPrefix(val.String(), prefix+"valaddr")) + bech32ValPub := types.MustBech32ifyValPub(pub) + require.True(t, strings.HasPrefix(bech32ValPub, prefix+"valpub")) + + config.SetBech32PrefixForConsensusNode(prefix+"consaddr", prefix+"conspub") + cons := types.ConsAddress(pub.Address()) + require.True(t, strings.HasPrefix(cons.String(), prefix+"consaddr")) + bech32ConsPub := types.MustBech32ifyConsPub(pub) + require.True(t, strings.HasPrefix(bech32ConsPub, prefix+"conspub")) + } + + } +} diff --git a/types/config.go b/types/config.go new file mode 100644 index 0000000000..da1a387fe1 --- /dev/null +++ b/types/config.go @@ -0,0 +1,105 @@ +package types + +import ( + "sync" +) + +// Config is the structure that holds the SDK configuration parameters. +// This could be used to initialize certain configuration parameters for the SDK. +type Config struct { + mtx sync.RWMutex + sealed bool + bech32AddressPrefix map[string]string +} + +var ( + // Initializing an instance of Config + sdkConfig = &Config{ + sealed: false, + bech32AddressPrefix: map[string]string{ + "account_addr": Bech32PrefixAccAddr, + "validator_addr": Bech32PrefixValAddr, + "consensus_addr": Bech32PrefixConsAddr, + "account_pub": Bech32PrefixAccPub, + "validator_pub": Bech32PrefixValPub, + "consensus_pub": Bech32PrefixConsPub, + }, + } +) + +// GetConfig returns the config instance for the SDK. +func GetConfig() *Config { + return sdkConfig +} + +func (config *Config) assertNotSealed() { + config.mtx.Lock() + defer config.mtx.Unlock() + + if config.sealed { + panic("Config is sealed") + } +} + +// SetBech32PrefixForAccount builds the Config with Bech32 addressPrefix and publKeyPrefix for accounts +// and returns the config instance +func (config *Config) SetBech32PrefixForAccount(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["account_addr"] = addressPrefix + config.bech32AddressPrefix["account_pub"] = pubKeyPrefix +} + +// SetBech32PrefixForValidator builds the Config with Bech32 addressPrefix and publKeyPrefix for validators +// and returns the config instance +func (config *Config) SetBech32PrefixForValidator(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["validator_addr"] = addressPrefix + config.bech32AddressPrefix["validator_pub"] = pubKeyPrefix +} + +// SetBech32PrefixForConsensusNode builds the Config with Bech32 addressPrefix and publKeyPrefix for consensus nodes +// and returns the config instance +func (config *Config) SetBech32PrefixForConsensusNode(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["consensus_addr"] = addressPrefix + config.bech32AddressPrefix["consensus_pub"] = pubKeyPrefix +} + +// Seal seals the config such that the config state could not be modified further +func (config *Config) Seal() *Config { + config.mtx.Lock() + defer config.mtx.Unlock() + + config.sealed = true + return config +} + +// GetBech32AccountAddrPrefix returns the Bech32 prefix for account address +func (config *Config) GetBech32AccountAddrPrefix() string { + return config.bech32AddressPrefix["account_addr"] +} + +// GetBech32ValidatorAddrPrefix returns the Bech32 prefix for validator address +func (config *Config) GetBech32ValidatorAddrPrefix() string { + return config.bech32AddressPrefix["validator_addr"] +} + +// GetBech32ConsensusAddrPrefix returns the Bech32 prefix for consensus node address +func (config *Config) GetBech32ConsensusAddrPrefix() string { + return config.bech32AddressPrefix["consensus_addr"] +} + +// GetBech32AccountPubPrefix returns the Bech32 prefix for account public key +func (config *Config) GetBech32AccountPubPrefix() string { + return config.bech32AddressPrefix["account_pub"] +} + +// GetBech32ValidatorPubPrefix returns the Bech32 prefix for validator public key +func (config *Config) GetBech32ValidatorPubPrefix() string { + return config.bech32AddressPrefix["validator_pub"] +} + +// GetBech32ConsensusPubPrefix returns the Bech32 prefix for consensus node public key +func (config *Config) GetBech32ConsensusPubPrefix() string { + return config.bech32AddressPrefix["consensus_pub"] +} From 1686ba072bfdcd87ab0526be6281b90c351f81ab Mon Sep 17 00:00:00 2001 From: Elvin Date: Thu, 1 Nov 2018 17:26:44 +0800 Subject: [PATCH 32/61] Merge PR #2643: AppendTag function usage error. append elements do not work --- PENDING.md | 2 ++ x/gov/handler.go | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/PENDING.md b/PENDING.md index 8a16136c09..4a13214b60 100644 --- a/PENDING.md +++ b/PENDING.md @@ -57,5 +57,7 @@ BUG FIXES * Gaia * SDK + - #2625 [x/gov] fix AppendTag function usage error + * Tendermint diff --git a/x/gov/handler.go b/x/gov/handler.go index 3f8cd312b6..dc1dbfb10e 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -42,7 +42,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos ) if votingStarted { - resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) + resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) } return sdk.Result{ @@ -68,7 +68,7 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result ) if votingStarted { - resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) + resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) } return sdk.Result{ @@ -111,8 +111,8 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(inactiveProposal.GetProposalID()) keeper.DeleteProposal(ctx, inactiveProposal) - resTags.AppendTag(tags.Action, tags.ActionProposalDropped) - resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.Action, tags.ActionProposalDropped) + resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) logger.Info( fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %v steak); deleted", @@ -152,8 +152,8 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) - resTags.AppendTag(tags.Action, action) - resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.Action, action) + resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) } return resTags From 3cffe29da7086ca93dabbcbf993e860777146ef0 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Thu, 1 Nov 2018 11:58:25 -0700 Subject: [PATCH 33/61] simulation: Make validator choice use validator set This also had to change the default seed, since with the previous one it actually got into a state where there were no validators left bonded, lol. This also changes Unbond msgs from failing with almost 100% probability to now only failing with 33% probability. Thus more of the state machine is getting tested! --- Makefile | 2 +- x/mock/simulation/util.go | 7 ++++++ x/stake/keeper/test_common.go | 15 +++++++++++++ x/stake/simulation/msgs.go | 40 ++++++++++++++++++----------------- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 9d4ad0a255..af4ea71c98 100644 --- a/Makefile +++ b/Makefile @@ -169,7 +169,7 @@ test_sim_gaia_nondeterminism: test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=10 -v -timeout 24h test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index 84a4ad2a33..d5a20b6e8b 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -2,6 +2,7 @@ package simulation import ( "fmt" + "math/big" "math/rand" "os" "sort" @@ -71,6 +72,12 @@ 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) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 7978f17e94..af7e688f32 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -3,6 +3,7 @@ package keeper import ( "bytes" "encoding/hex" + "math/rand" "strconv" "testing" @@ -215,3 +216,17 @@ func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { store := ctx.KVStore(k.storeKey) return store.Has(power) } + +// RandomValidator returns a random validator given access to the keeper and ctx +func RandomValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator { + vals := keeper.GetAllValidators(ctx) + i := r.Intn(len(vals)) + return vals[i] +} + +// RandomBondedValidator returns a random bonded validator given access to the keeper and ctx +func RandomBondedValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator { + vals := keeper.GetBondedValidatorsByPower(ctx) + i := r.Intn(len(vals)) + return vals[i] +} diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index 8684126e30..ec2dffd927 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/keeper" abci "github.com/tendermint/tendermint/abci/types" ) @@ -87,8 +88,8 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { maxCommission := sdk.NewInt(10) newCommissionRate := sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1) - acc := simulation.RandomAcc(r, accs) - address := sdk.ValAddress(acc.Address) + val := keeper.RandomValidator(r, k, ctx) + address := val.GetOperator() msg := stake.MsgEditValidator{ Description: description, ValidatorAddr: address, @@ -118,8 +119,8 @@ func SimulateMsgDelegate(m auth.AccountKeeper, k stake.Keeper) simulation.Operat action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - validatorAcc := simulation.RandomAcc(r, accs) - validatorAddress := sdk.ValAddress(validatorAcc.Address) + val := keeper.RandomValidator(r, k, ctx) + validatorAddress := val.GetOperator() delegatorAcc := simulation.RandomAcc(r, accs) delegatorAddress := delegatorAcc.Address amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) @@ -155,25 +156,26 @@ func SimulateMsgBeginUnbonding(m auth.AccountKeeper, k stake.Keeper) simulation. accs []simulation.Account, event func(string)) ( action string, fOp []simulation.FutureOperation, err error) { - denom := k.GetParams(ctx).BondDenom - validatorAcc := simulation.RandomAcc(r, accs) - validatorAddress := sdk.ValAddress(validatorAcc.Address) delegatorAcc := simulation.RandomAcc(r, accs) delegatorAddress := delegatorAcc.Address - amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) - if amount.GT(sdk.ZeroInt()) { - amount = simulation.RandomAmount(r, amount) + delegations := k.GetAllDelegatorDelegations(ctx, delegatorAddress) + if len(delegations) == 0 { + return "no-operation", nil, nil } - if amount.Equal(sdk.ZeroInt()) { + delegation := delegations[r.Intn(len(delegations))] + + numShares := simulation.RandomDecAmount(r, delegation.Shares) + if numShares.Equal(sdk.ZeroDec()) { return "no-operation", nil, nil } msg := stake.MsgBeginUnbonding{ DelegatorAddr: delegatorAddress, - ValidatorAddr: validatorAddress, - SharesAmount: sdk.NewDecFromInt(amount), + ValidatorAddr: delegation.ValidatorAddr, + SharesAmount: numShares, } if msg.ValidateBasic() != nil { - return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s, got error %v", + msg.GetSignBytes(), msg.ValidateBasic()) } ctx, write := ctx.CacheContext() result := handler(ctx, msg) @@ -194,10 +196,10 @@ func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k stake.Keeper) simulation action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - sourceValidatorAcc := simulation.RandomAcc(r, accs) - sourceValidatorAddress := sdk.ValAddress(sourceValidatorAcc.Address) - destValidatorAcc := simulation.RandomAcc(r, accs) - destValidatorAddress := sdk.ValAddress(destValidatorAcc.Address) + srcVal := keeper.RandomValidator(r, k, ctx) + srcValidatorAddress := srcVal.GetOperator() + destVal := keeper.RandomValidator(r, k, ctx) + destValidatorAddress := destVal.GetOperator() delegatorAcc := simulation.RandomAcc(r, accs) delegatorAddress := delegatorAcc.Address // TODO @@ -210,7 +212,7 @@ func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k stake.Keeper) simulation } msg := stake.MsgBeginRedelegate{ DelegatorAddr: delegatorAddress, - ValidatorSrcAddr: sourceValidatorAddress, + ValidatorSrcAddr: srcValidatorAddress, ValidatorDstAddr: destValidatorAddress, SharesAmount: sdk.NewDecFromInt(amount), } From 52083e105b37b7686ce50ae3c275942a91d5c3ef Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Thu, 1 Nov 2018 12:52:03 -0700 Subject: [PATCH 34/61] Update changelog --- PENDING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 8a16136c09..2606aa0fff 100644 --- a/PENDING.md +++ b/PENDING.md @@ -41,7 +41,8 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance - - \#1924 [simulation] Use a transition matrix for block size + - \#1924 [x/mock/simulation] Use a transition matrix for block size + - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - #2610 [x/stake] Block redelegation to and from the same validator From ddba4fc81099b83213b14179de60d0ec997be952 Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 1 Nov 2018 18:12:22 -0400 Subject: [PATCH 35/61] Merge PR #2657: Fix config.js --- docs/.vuepress/config.js | 4 +- docs/config.js | 86 ---------------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 docs/config.js diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 5f09b5bb0c..4746b3dd44 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -72,8 +72,8 @@ module.exports = { title: "Light Client", collapsable: false, children: [ - "/light/", - "/light/getting_started" + "/lite/", + "/lite/getting_started" ] }, { diff --git a/docs/config.js b/docs/config.js deleted file mode 100644 index 93426bcc5f..0000000000 --- a/docs/config.js +++ /dev/null @@ -1,86 +0,0 @@ -module.exports = { - title: "Cosmos Network", - description: "Documentation for the Cosmos Network.", - dest: "./dist/docs", - base: "/docs/", - markdown: { - lineNumbers: true - }, - themeConfig: { - lastUpdated: "Last Updated", - nav: [{ text: "Back to Cosmos", link: "https://cosmos.network" }], - sidebar: [ - { - title: "Introduction", - collapsable: false, - children: [ - "/introduction/cosmos-hub", - "/introduction/tendermint", - ] - }, - { - title: "Getting Started", - collapsable: false, - children: [ - "/getting-started/voyager", - "/getting-started/installation", - "/getting-started/full-node", - "/getting-started/create-testnet" - ] - }, - { - title: "Cosmos SDK", - collapsable: false, - children: [ - ["/sdk/overview", "Overview"], - ["/sdk/core/intro", "Core"], - "/sdk/core/app1", - "/sdk/core/app2", - "/sdk/core/app3", - "/sdk/core/app4", - "/sdk/core/app5", - // "/sdk/modules", - "/sdk/clients" - ] - }, - // { - // title: "Specifications", - // collapsable: false, - // children: [ - // ["/specs/overview", "Overview"], - // "/specs/governance", - // "/specs/ibc", - // "/specs/staking", - // "/specs/icts", - // ] - // }, - { - title: "Lotion JS", - collapsable: false, - children: [["/lotion/overview", "Overview"], "/lotion/building-an-app"] - }, - { - title: "Validators", - collapsable: false, - children: [ - ["/validators/overview", "Overview"], - ["/validators/security", "Security"], - ["/validators/validator-setup", "Validator Setup"], - "/validators/validator-faq" - ] - }, - { - title: "Resources", - collapsable: false, - children: [ - // ["/resources/faq" "General"], - "/resources/delegator-faq", - ["/resources/whitepaper", "Whitepaper - English"], - ["/resources/whitepaper-ko", "Whitepaper - 한국어"], - ["/resources/whitepaper-zh-CN", "Whitepaper - 中文"], - ["/resources/whitepaper-pt", "Whitepaper - Português"] - ] - } - ] - } -} From c5238b0d1ecfef8be3ccdaee02d23ee93ef9c69b Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 1 Nov 2018 23:13:40 +0100 Subject: [PATCH 36/61] Merge PR #2589: Update Vesting Spec --- docs/spec/auth/vesting.md | 522 ++++++++++++++++++++++++++++++-------- 1 file changed, 416 insertions(+), 106 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index c5c25ecaed..b8785619b2 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -1,159 +1,469 @@ -## Vesting +# Vesting -### Intro and Requirements + -This paper specifies vesting account implementation for the Cosmos Hub. -The requirements for this vesting account is that it should be initialized during genesis with -a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators -and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. -The vesting account should also be able to spend any coins it receives from other users. -Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their -unlocked coin amount. +- [Vesting](#vesting) + - [Intro and Requirements](#intro-and-requirements) + - [Vesting Account Types](#vesting-account-types) + - [Vesting Account Specification](#vesting-account-specification) + - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) + - [Continuously Vesting Accounts](#continuously-vesting-accounts) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) + - [Transferring/Sending](#transferringsending) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-1) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-1) + - [Keepers/Handlers](#keepershandlers) + - [Delegating](#delegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-2) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-2) + - [Keepers/Handlers](#keepershandlers-1) + - [Undelegating](#undelegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-3) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-3) + - [Keepers/Handlers](#keepershandlers-2) + - [Keepers & Handlers](#keepers--handlers) + - [Initializing at Genesis](#initializing-at-genesis) + - [Examples](#examples) + - [Simple](#simple) + - [Slashing](#slashing) + - [Glossary](#glossary) -### Implementation + -##### Vesting Account implementation +## Intro and Requirements -NOTE: `Now = ctx.BlockHeader().Time` +This paper specifies vesting account implementation for the Cosmos Hub. +The requirements for this vesting account is that it should be initialized +during genesis with a starting balance `X` coins and a vesting end time `T`. + +The owner of this account should be able to delegate to validators +and vote with locked coins, however they cannot send locked coins to other +accounts until those coins have been unlocked. When it comes to governance, it +is yet undefined if we want to allow a vesting account to be able to deposit +vesting coins into proposals. + +In addition, a vesting account vests all of its coin denominations at the same +rate. This may be subject to change. + +**Note**: A vesting account could have some vesting and non-vesting coins. To +support such a feature, the `GenesisAccount` type will need to be updated in +order to make such a distinction. + +## Vesting Account Types ```go +// VestingAccount defines an interface that any vesting account type must +// implement. type VestingAccount interface { Account - AssertIsVestingAccount() // existence implies that account is vesting. + AssertIsVestingAccount() // existence implies that account is vesting - // Calculates amount of coins that can be sent to other accounts given the current time - SendableCoins(sdk.Context) sdk.Coins + // Calculates the amount of coins that can be sent to other accounts given + // the current time. + SpendableCoins(Context) Coins + // Performs delegation accounting. + TrackDelegation(amount) + // Performs undelegation accounting. + TrackUndelegation(amount) } -// Implements Vesting Account -// Continuously vests by unlocking coins linearly with respect to time +// BaseVestingAccount implements the VestingAccount interface. It contains all +// the necessary fields needed for any vesting account implementation. +type BaseVestingAccount struct { + BaseAccount + + OriginalVesting Coins // coins in account upon initialization + DelegatedFree Coins // coins that are vested and delegated + EndTime Time // when the coins become unlocked +} + +// ContinuousVestingAccount implements the VestingAccount interface. It +// continuously vests by unlocking coins linearly with respect to time. type ContinuousVestingAccount struct { BaseAccount - OriginalVestingCoins sdk.Coins // Coins in account on Initialization - ReceivedCoins sdk.Coins // Coins received from other accounts - SentCoins sdk.Coins // Coins sent to other accounts + BaseVestingAccount - // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point - StartTime time.Time - EndTime time.Time + DelegatedVesting Coins // coins that vesting and delegated + StartTime Time // when the coins start to vest } -// Uses time in context to calculate total unlocked coins -SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: - - // Coins unlocked by vesting schedule - unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime) - - // Must still check for currentCoins constraint since some unlocked coins may have been delegated. - currentCoins := vacc.BaseAccount.GetCoins() - - // min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins - return min(unlockedCoins, currentCoins) - +// DelayedVestingAccount implements the VestingAccount interface. It vests all +// coins after a specific time, but non prior. In other words, it keeps them +// locked until a specified time. +type DelayedVestingAccount struct { + BaseAccount + BaseVestingAccount +} ``` -The `VestingAccount` interface is used to assert that an account is a vesting account like so: +## Vesting Account Specification + +Given a vesting account, we define the following in the proceeding operations: + +- `OV`: The original vesting coin amount. It is a constant value. +- `V`: The number of `OV` coins that are still _vesting_. It is derived by `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis. +- `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is computed on demand and not a per-block basis. +- `DV`: The number of delegated _vesting_ coins. It is a variable value. It is stored and modified directly in the vesting account. +- `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable value. It is stored and modified directly in the vesting account. +- `BC`: The number of `OV` coins less any coins that are transferred, which can be negative, or delegated (`DV + DF`). It is considered to be balance of the embedded base account. It is stored and modified directly in the vesting account. + +### Determining Vesting & Vested Amounts + +It is important to note that these values are computed on demand and not on a +mandatory per-block basis. + +#### Continuously Vesting Accounts + +To determine the amount of coins that are vested for a given block `B`, the +following is performed: + +1. Compute `X := B.Time - StartTime` +2. Compute `Y := EndTime - StartTime` +3. Compute `V' := OV * (X / Y)` +4. Compute `V := OV - V'` + +Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`, +is _vesting_. ```go -vacc, ok := acc.(VestingAccount); ok +func (cva ContinuousVestingAccount) GetVestedCoins(b Block) Coins { + // We must handle the case where the start time for a vesting account has + // been set into the future or when the start of the chain is not exactly + // known. + if b.Time < va.StartTime { + return ZeroCoins + } + + x := b.Time - cva.StartTime + y := cva.EndTime - cva.StartTime + + return cva.OriginalVesting * (x / y) +} + +func (cva ContinuousVestingAccount) GetVestingCoins(b Block) Coins { + return cva.OriginalVesting - cva.GetVestedCoins(b) +} ``` -as well as to calculate the SendableCoins at any given moment. +#### Delayed/Discrete Vesting Accounts -The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`, -`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. -Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements -the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of -both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period. - -##### Changes to Keepers/Handler - -Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be -handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like -`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. +Delayed vesting accounts are easier to reason about as they only have the full +amount vesting up until a certain time, then they all become vested (unlocked). ```go -if acc is VestingAccount and Now < vestingAccount.EndTime: - // Check if amount is less than currently allowed sendable coins - if msg.Amount > vestingAccount.SendableCoins(ctx) then fail - else: - vestingAccount.SentCoins += msg.Amount +func (dva DelayedVestingAccount) GetVestedCoins(b Block) Coins { + if b.Time >= dva.EndTime { + return dva.OriginalVesting + } -else: - // Account has fully vested, treat like regular account - if msg.Amount > account.GetCoins() then fail - -// All checks passed, send the coins -SendCoins(inputs, outputs) + return ZeroCoins +} +func (dva DelayedVestingAccount) GetVestingCoins(b Block) Coins { + return cva.OriginalVesting - cva.GetVestedCoins(b) +} ``` -Coins that are sent to a vesting account after initialization by users sending them coins should be spendable -immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not -originally own should increment `ReceivedCoins` by the amount sent. -Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute. +### Transferring/Sending -CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`. -However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. +#### Continuously Vesting Accounts -### Initializing at Genesis +At any given time, a continuous vesting account may transfer: `min((BC + DV) - V, BC)`. -To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be -BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts -as appropriate. +In other words, a vesting account may transfer the minimum of the base account +balance and the base account balance plus the number of currently delegated +vesting coins less the number of coins vested so far. + +```go +func (cva ContinuousVestingAccount) SpendableCoins() Coins { + bc := cva.GetCoins() + return min((bc + cva.DelegatedVesting) - cva.GetVestingCoins(), bc) +} +``` + +##### Delayed/Discrete Vesting Accounts + +A delayed vesting account may send any coins it has received. In addition, if it +has fully vested, it can send any of it's vested coins. + +```go +func (dva DelayedVestingAccount) SpendableCoins() Coins { + bc := dva.GetCoins() + return bc - dva.GetVestingCoins() +} +``` + +##### Keepers/Handlers + +The corresponding `x/bank` keeper should appropriately handle sending coins +based on if the account is a vesting account or not. + +```go +func SendCoins(from Account, to Account amount Coins) { + if isVesting(from) { + sc := from.SpendableCoins() + } else { + sc := from.GetCoins() + } + + if amount <= sc { + from.SetCoins(sc - amount) + to.SetCoins(amount) + // save accounts... + } +} +``` + +### Delegating + +#### Continuously Vesting Accounts + +For a continuous vesting account attempting to delegate `D` coins, the following +is performed: + +1. Verify `BC >= D > 0` +2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting) +3. Compute `Y := D - X` (portion of `D` that is free) +4. Set `DV += X` +5. Set `DF += Y` +6. Set `BC -= D` + +```go +func (cva ContinuousVestingAccount) TrackDelegation(amount Coins) { + x := min(max(cva.GetVestingCoins() - cva.DelegatedVesting, 0), amount) + y := amount - x + + cva.DelegatedVesting += x + cva.DelegatedFree += y +} +``` + +##### Delayed/Discrete Vesting Accounts + +For a delayed vesting account, it can only delegate with received coins and +coins that are fully vested so we only need to update `DF`. + +```go +func (dva DelayedVestingAccount) TrackDelegation(amount Coins) { + dva.DelegatedFree += amount +} +``` + +##### Keepers/Handlers + +```go +func DelegateCoins(from Account, amount Coins) { + // canDelegate checks different semantics for continuous and delayed vesting + // accounts + if isVesting(from) && canDelegate(from) { + sc := from.GetCoins() + + if amount <= sc { + from.TrackDelegation(amount) + from.SetCoins(sc - amount) + // save account... + } + } else { + sc := from.GetCoins() + + if amount <= sc { + from.SetCoins(sc - amount) + // save account... + } + } +} +``` + +### Undelegating + +#### Continuously Vesting Accounts + +For a continuous vesting account attempting to undelegate `D` coins, the +following is performed: + +1. Verify `(DV + DF) >= D > 0` (this is simply a sanity check) +2. Compute `Y := min(DF, D)` (portion of `D` that should become free, prioritizing free coins) +3. Compute `X := D - Y` (portion of `D` that should remain vesting) +4. Set `DV -= X` +5. Set `DF -= Y` +6. Set `BC += D` + +```go +func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) { + y := min(cva.DelegatedFree, amount) + x := amount - y + + cva.DelegatedVesting -= x + cva.DelegatedFree -= y +} +``` + +**Note**: If a delegation is slashed, the continuous vesting account will end up +with excess an `DV` amount, even after all its coins have vested. This is because +undelegating free coins are prioritized. + +##### Delayed/Discrete Vesting Accounts + +For a delayed vesting account, it only needs to add back the `DF` amount since +the account is fully vested. + +```go +func (dva DelayedVestingAccount) TrackUndelegation(amount Coins) { + dva.DelegatedFree -= amount +} +``` + +##### Keepers/Handlers + +```go +func UndelegateCoins(to Account, amount Coins) { + if isVesting(to) { + if to.DelegatedFree + to.DelegatedVesting >= amount { + to.TrackUndelegation(amount) + AddCoins(to, amount) + // save account ... + } + } else { + AddCoins(to, amount) + // save account... + } +} +``` + +## Keepers & Handlers + +The `VestingAccount` implementations reside in `x/auth`. However, any keeper in +a module (e.g. staking in `x/stake`) wishing to potentially utilize any vesting +coins, must call explicit methods on the `x/bank` keeper (e.g. `DelegateCoins`) +opposed to `SendCoins` and `SubtractCoins`. + +In addition, the vesting account should also be able to spend any coins it +receives from other users. Thus, the bank module's `MsgSend` handler should +error if a vesting account is trying to send an amount that exceeds their +unlocked coin amount. + +See the above specification for full implementation details. + +## Initializing at Genesis + +To initialize both vesting accounts and base accounts, the `GenesisAccount` +struct will include an `EndTime`. Accounts meant to be of type `BaseAccount` will +have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into +BaseAccounts and VestingAccounts as appropriate. ```go type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - GenesisCoins sdk.Coins `json:"coins"` - EndTime int64 `json:"lock"` + Address sdk.AccAddress + GenesisCoins sdk.Coins + EndTime int64 } -initChainer: - for gacc in GenesisAccounts: +func initChainer() { + for genAcc in GenesisAccounts { baseAccount := BaseAccount{ - Address: gacc.Address, - Coins: gacc.GenesisCoins, + Address: genAcc.Address, + Coins: genAcc.GenesisCoins, } - if gacc.EndTime != 0: - vestingAccount := ContinuouslyVestingAccount{ - BaseAccount: baseAccount, - OriginalVestingCoins: gacc.GenesisCoins, - StartTime: RequestInitChain.Time, - EndTime: gacc.EndTime, - } - AddAccountToState(vestingAccount) - else: - AddAccountToState(baseAccount) + if genAcc.EndTime != 0 { + vestingAccount := ContinuousVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: genAcc.GenesisCoins, + StartTime: RequestInitChain.Time, + EndTime: genAcc.EndTime, + } + + AddAccountToState(vestingAccount) + } else { + AddAccountToState(baseAccount) + } + } +} ``` -### Formulas +## Examples -`OriginalVestingCoins`: Amount of coins in account at Genesis +### Simple -`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) +Given a continuous vesting account with 10 vesting coins. -`ReceivedCoins`: Coins received from other accounts (always unlocked) +``` +OV = 10 +DF = 0 +DV = 0 +BC = 10 +V = 10 +V' = 0 +``` -`LockedCoins`: Coins that are currently locked +1. Immediately receives 1 coin + ``` + BC = 11 + ``` +2. Time passes, 2 coins vest + ``` + V = 8 + V' = 2 + ``` +3. Delegates 4 coins to validator A + ``` + DV = 4 + BC = 7 + ``` +4. Sends 3 coins + ``` + BC = 4 + ``` +5. More time passes, 2 more coins vest + ``` + V = 6 + V' = 4 + ``` +6. Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate. + ``` + BC = 2 + ``` -`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked) +### Slashing -`Sent`: Coins sent to other accounts (MUST be unlocked) +Same initial starting conditions as the simple example. -Maximum amount of coins vesting schedule allows to be sent: +1. Time passes, 5 coins vest + ``` + V = 5 + V' = 5 + ``` +2. Delegate 5 coins to validator A + ``` + DV = 5 + BC = 5 + ``` +3. Delegate 5 coins to validator B + ``` + DF = 5 + BC = 0 + ``` +4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins +5. Undelegate from validator A (2.5 coins) + ``` + DF = 5 - 2.5 = 2.5 + BC = 0 + 2.5 = 2.5 + ``` +6. Undelegate from validator B (5 coins). The account at this point can only send 2.5 coins unless it receives more coins or until more coins vest. It can still however, delegate. + ``` + DV = 5 - 2.5 = 2.5 + DF = 2.5 - 2.5 = 0 + BC = 2.5 + 5 = 7.5 + ``` -`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)` +Notice how we have an excess amount of `DV`. -`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins` +## Glossary -Coins currently in Account: - -`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent` - -`CurrentCoins = vestingAccount.GetCoins()` - -**Maximum amount of coins spendable right now:** - -`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )` +- OriginalVesting: The amount of coins (per denomination) that are initially part of a vesting account. These coins are set at genesis. +- StartTime: The BFT time at which a vesting account starts to vest. +- EndTime: The BFT time at which a vesting account is fully vested. +- DelegatedFree: The tracked amount of coins (per denomination) that are delegated from a vesting account that have been fully vested at time of delegation. +- DelegatedVesting: The tracked amount of coins (per denomination) that are delegated from a vesting account that were vesting at time of delegation. +- ContinuousVestingAccount: A vesting account implementation that vests coins linearly over time. +- DelayedVestingAccount: A vesting account implementation that only fully vests all coins at a given time. From 3657ff4ada102a296de96f29513c46a8e6443844 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 1 Nov 2018 23:29:21 +0000 Subject: [PATCH 37/61] Merge PR #2656: Revert read-only leveldb database * Revert read-only leveldb database Waiting on a fix for syndtr/goleveldb#240. * Update client/keys/utils.go --- client/keys/utils.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/keys/utils.go b/client/keys/utils.go index 8a7eefea38..742b512d3d 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -96,7 +96,12 @@ func GetKeyBaseFromDirWithWritePerm(rootDir string) (keys.Keybase, error) { // GetKeyBaseFromDir initializes a read-only keybase at a particular dir. func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) { - return getKeyBaseFromDirWithOpts(rootDir, &opt.Options{ReadOnly: true}) + // Disabled because of the inability to create a new keys database directory + // in the instance of when ReadOnly is set to true. + // + // ref: syndtr/goleveldb#240 + // return getKeyBaseFromDirWithOpts(rootDir, &opt.Options{ReadOnly: true}) + return getKeyBaseFromDirWithOpts(rootDir, nil) } func getKeyBaseFromDirWithOpts(rootDir string, o *opt.Options) (keys.Keybase, error) { From 91dcd0f82afb133e74ea9e852e472205a5fa50f4 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 2 Nov 2018 08:17:57 +0000 Subject: [PATCH 38/61] Include DNS alt name in certificate Closes: #2664 --- client/lcd/certificates.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go index f47f2397c7..0ec527e134 100644 --- a/client/lcd/certificates.go +++ b/client/lcd/certificates.go @@ -38,12 +38,13 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK Subject: pkix.Name{ Organization: []string{"Gaia Lite"}, }, + DNSNames: []string{"localhost"}, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, - IsCA: true, + IsCA: true, } hosts := strings.Split(host, ",") for _, h := range hosts { From 48192ea3d465fbcd3f1190838841021c91b91171 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 2 Nov 2018 08:42:29 +0000 Subject: [PATCH 39/61] Gaialite signal handling is broken, repair it --- client/lcd/root.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/client/lcd/root.go b/client/lcd/root.go index 475186ed0a..8b2fb4206c 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -5,6 +5,8 @@ import ( "net" "net/http" "os" + "os/signal" + "syscall" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -21,7 +23,6 @@ import ( "github.com/rakyll/statik/fs" "github.com/spf13/cobra" "github.com/spf13/viper" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" tmserver "github.com/tendermint/tendermint/rpc/lib/server" ) @@ -57,6 +58,25 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { var listener net.Listener var fingerprint string + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + closeListener := func() { + err := listener.Close() + logger.Error("error closing listener", "err", err) + } + go func() { + sig := <-sigs + switch sig { + case syscall.SIGTERM: + defer closeListener() + os.Exit(128 + int(syscall.SIGTERM)) + case syscall.SIGINT: + defer closeListener() + os.Exit(128 + int(syscall.SIGINT)) + } + }() + if viper.GetBool(flagInsecure) { listener, err = tmserver.StartHTTPServer( listenAddr, handler, logger, @@ -89,6 +109,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { } defer cleanupFunc() } + listener, err = tmserver.StartHTTPAndTLSServer( listenAddr, handler, certFile, keyFile, @@ -102,13 +123,6 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { } logger.Info("REST server started") - // wait forever and cleanup - cmn.TrapSignal(func() { - defer cleanupFunc() - err := listener.Close() - logger.Error("error closing listener", "err", err) - }) - return nil }, } From 8e328c870685ff21eba4c120fe12eb4351a1eb43 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Fri, 2 Nov 2018 05:35:21 -0700 Subject: [PATCH 40/61] Merge PR #2665: simulation: Remove header from Invariant This got introduced recently, but wasn't actually needed, hence the reversion --- PENDING.md | 1 + x/bank/simulation/invariants.go | 4 ++-- x/distribution/simulation/invariants.go | 10 +++++----- x/gov/simulation/invariants.go | 3 +-- x/mock/simulation/random_simulate_blocks.go | 14 +++++++------- x/mock/simulation/types.go | 6 +++--- x/mock/simulation/util.go | 5 ++--- x/slashing/simulation/invariants.go | 3 +-- x/stake/simulation/invariants.go | 14 +++++++------- 9 files changed, 29 insertions(+), 31 deletions(-) diff --git a/PENDING.md b/PENDING.md index e7451a2d4d..dc13cfc9fb 100644 --- a/PENDING.md +++ b/PENDING.md @@ -9,6 +9,7 @@ BREAKING CHANGES * Gaia * SDK + * [simulation] \#2665 only argument to simulation.Invariant is now app * Tendermint diff --git a/x/bank/simulation/invariants.go b/x/bank/simulation/invariants.go index 6574407a4c..f0cd5ba63c 100644 --- a/x/bank/simulation/invariants.go +++ b/x/bank/simulation/invariants.go @@ -14,7 +14,7 @@ import ( // NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) accts := mock.GetAllAccounts(mapper, ctx) for _, acc := range accts { @@ -32,7 +32,7 @@ func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant // TotalCoinsInvariant checks that the sum of the coins across all accounts // is what is expected func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) totalCoins := sdk.Coins{} diff --git a/x/distribution/simulation/invariants.go b/x/distribution/simulation/invariants.go index 45c015cf07..75863bfdb4 100644 --- a/x/distribution/simulation/invariants.go +++ b/x/distribution/simulation/invariants.go @@ -13,9 +13,8 @@ import ( // AllInvariants runs all invariants of the distribution module // Currently: total supply, positive power func AllInvariants(d distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { - - return func(app *baseapp.BaseApp, header abci.Header) error { - err := ValAccumInvariants(d, sk)(app, header) + return func(app *baseapp.BaseApp) error { + err := ValAccumInvariants(d, sk)(app) if err != nil { return err } @@ -26,8 +25,9 @@ func AllInvariants(d distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { // ValAccumInvariants checks that the fee pool accum == sum all validators' accum func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { - return func(app *baseapp.BaseApp, header abci.Header) error { - ctx := app.NewContext(false, header) + return func(app *baseapp.BaseApp) error { + mockHeader := abci.Header{Height: app.LastBlockHeight() + 1} + ctx := app.NewContext(false, mockHeader) height := ctx.BlockHeight() valAccum := sdk.ZeroDec() diff --git a/x/gov/simulation/invariants.go b/x/gov/simulation/invariants.go index 3135ac80cb..6d5f419184 100644 --- a/x/gov/simulation/invariants.go +++ b/x/gov/simulation/invariants.go @@ -3,12 +3,11 @@ package simulation import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/x/mock/simulation" - abci "github.com/tendermint/tendermint/abci/types" ) // AllInvariants tests all governance invariants func AllInvariants() simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { // TODO Add some invariants! // Checking proposal queues, no passed-but-unexecuted proposals, etc. return nil diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 36c41fdd11..a4da39523e 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -3,7 +3,6 @@ package simulation import ( "encoding/json" "fmt" - "math" "math/rand" "os" "os/signal" @@ -49,7 +48,8 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap } func randTimestamp(r *rand.Rand) time.Time { - unixTime := r.Int63n(int64(math.Pow(2, 40))) + // json.Marshal breaks for timestamps greater with year greater than 9999 + unixTime := r.Int63n(253373529600) return time.Unix(unixTime, 0) } @@ -138,7 +138,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, if testingMode { // Make sure invariants hold at beginning of block - assertAllInvariants(t, app, header, invariants, "BeginBlock", displayLogs) + assertAllInvariants(t, app, invariants, "BeginBlock", displayLogs) } ctx := app.NewContext(false, header) @@ -149,7 +149,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, 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, header, invariants, "QueuedOperations", displayLogs) + assertAllInvariants(t, app, invariants, "QueuedOperations", displayLogs) } logWriter("Standard operations") @@ -157,7 +157,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan if testingMode { // Make sure invariants hold at end of block - assertAllInvariants(t, app, header, invariants, "StandardOperations", displayLogs) + assertAllInvariants(t, app, invariants, "StandardOperations", displayLogs) } res := app.EndBlock(abci.RequestEndBlock{}) @@ -168,7 +168,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, if testingMode { // Make sure invariants hold at end of block - assertAllInvariants(t, app, header, invariants, "EndBlock", displayLogs) + assertAllInvariants(t, app, invariants, "EndBlock", displayLogs) } if commit { app.Commit() @@ -243,7 +243,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, queueOperations(operationQueue, timeOperationQueue, futureOps) if testingMode { if onOperation { - assertAllInvariants(t, app, header, invariants, fmt.Sprintf("operation: %v", logUpdate), displayLogs) + 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) diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index 0a7c989a5c..e601f2e1f9 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -33,7 +33,7 @@ type ( // 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, header abci.Header) error + Invariant func(app *baseapp.BaseApp) error // Account contains a privkey, pubkey, address tuple // eventually more useful data can be placed in here. @@ -73,9 +73,9 @@ type ( // 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, header abci.Header) error { + return func(app *baseapp.BaseApp) error { if int(app.LastBlockHeight())%period == offset { - return invariant(app, header) + return invariant(app) } return nil } diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index d5a20b6e8b..62b253a258 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -10,7 +10,6 @@ import ( "testing" "time" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" @@ -110,11 +109,11 @@ func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height } // assertAllInvariants asserts a list of provided invariants against application state -func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, header abci.Header, +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, header) + err := invariants[i](app) if err != nil { fmt.Printf("Invariants broken after %s\n", where) fmt.Println(err.Error()) diff --git a/x/slashing/simulation/invariants.go b/x/slashing/simulation/invariants.go index 5ad2d65ad2..0aa0ed1e5c 100644 --- a/x/slashing/simulation/invariants.go +++ b/x/slashing/simulation/invariants.go @@ -3,13 +3,12 @@ package simulation import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/x/mock/simulation" - abci "github.com/tendermint/tendermint/abci/types" ) // TODO Any invariants to check here? // AllInvariants tests all slashing invariants func AllInvariants() simulation.Invariant { - return func(_ *baseapp.BaseApp, _ abci.Header) error { + return func(_ *baseapp.BaseApp) error { return nil } } diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 3b97bdb72f..8e6f184321 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -19,16 +19,16 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant { - return func(app *baseapp.BaseApp, header abci.Header) error { - err := SupplyInvariants(ck, k, f, d, am)(app, header) + return func(app *baseapp.BaseApp) error { + err := SupplyInvariants(ck, k, f, d, am)(app) if err != nil { return err } - err = PositivePowerInvariant(k)(app, header) + err = PositivePowerInvariant(k)(app) if err != nil { return err } - err = ValidatorSetInvariant(k)(app, header) + err = ValidatorSetInvariant(k)(app) return err } } @@ -37,7 +37,7 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, // nolint: unparam func SupplyInvariants(ck bank.Keeper, k stake.Keeper, f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) pool := k.GetPool(ctx) @@ -102,7 +102,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, // PositivePowerInvariant checks that all stored validators have > 0 power func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) var err error k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { @@ -118,7 +118,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { // ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set func ValidatorSetInvariant(k stake.Keeper) simulation.Invariant { - return func(app *baseapp.BaseApp, _ abci.Header) error { + return func(app *baseapp.BaseApp) error { // TODO return nil } From b6443e949626d48d71707c688e03fb7bb8cbdb94 Mon Sep 17 00:00:00 2001 From: yutianwu Date: Fri, 2 Nov 2018 20:36:52 +0800 Subject: [PATCH 41/61] Merge PR #2653: Add benchmark for get and set account --- PENDING.md | 2 +- x/auth/mapper_test.go | 93 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/PENDING.md b/PENDING.md index dc13cfc9fb..84a08a5abe 100644 --- a/PENDING.md +++ b/PENDING.md @@ -45,7 +45,7 @@ IMPROVEMENTS - \#1924 [x/mock/simulation] Use a transition matrix for block size - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - #2610 [x/stake] Block redelegation to and from the same validator - + - #2652 [x/auth] Add benchmark for get and set account * Tendermint diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index 326f83dcca..f1f4c7eaf9 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -3,15 +3,13 @@ package auth import ( "testing" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - codec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" ) func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { @@ -115,9 +113,92 @@ func BenchmarkAccountMapperGetAccountFound(b *testing.B) { acc := mapper.NewAccountWithAddress(ctx, addr) mapper.SetAccount(ctx, acc) } + b.ResetTimer() for i := 0; i < b.N; i++ { arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} mapper.GetAccount(ctx, sdk.AccAddress(arr)) } } + +func BenchmarkAccountMapperGetAccountFoundWithCoins(b *testing.B) { + ms, capKey, _ := setupMultiStore() + cdc := codec.New() + RegisterBaseAccount(cdc) + + // make context and mapper + ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger()) + mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount) + + coins := sdk.Coins{ + sdk.NewCoin("LTC", sdk.NewInt(1000)), + sdk.NewCoin("BTC", sdk.NewInt(1000)), + sdk.NewCoin("ETH", sdk.NewInt(1000)), + sdk.NewCoin("XRP", sdk.NewInt(1000)), + sdk.NewCoin("BCH", sdk.NewInt(1000)), + sdk.NewCoin("EOS", sdk.NewInt(1000)), + } + + // assumes b.N < 2**24 + for i := 0; i < b.N; i++ { + arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} + addr := sdk.AccAddress(arr) + acc := mapper.NewAccountWithAddress(ctx, addr) + acc.SetCoins(coins) + mapper.SetAccount(ctx, acc) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} + mapper.GetAccount(ctx, sdk.AccAddress(arr)) + } +} + +func BenchmarkAccountMapperSetAccount(b *testing.B) { + ms, capKey, _ := setupMultiStore() + cdc := codec.New() + RegisterBaseAccount(cdc) + + // make context and mapper + ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger()) + mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount) + + b.ResetTimer() + // assumes b.N < 2**24 + for i := 0; i < b.N; i++ { + arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} + addr := sdk.AccAddress(arr) + acc := mapper.NewAccountWithAddress(ctx, addr) + mapper.SetAccount(ctx, acc) + } +} + +func BenchmarkAccountMapperSetAccountWithCoins(b *testing.B) { + ms, capKey, _ := setupMultiStore() + cdc := codec.New() + RegisterBaseAccount(cdc) + + // make context and mapper + ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger()) + mapper := NewAccountKeeper(cdc, capKey, ProtoBaseAccount) + + coins := sdk.Coins{ + sdk.NewCoin("LTC", sdk.NewInt(1000)), + sdk.NewCoin("BTC", sdk.NewInt(1000)), + sdk.NewCoin("ETH", sdk.NewInt(1000)), + sdk.NewCoin("XRP", sdk.NewInt(1000)), + sdk.NewCoin("BCH", sdk.NewInt(1000)), + sdk.NewCoin("EOS", sdk.NewInt(1000)), + } + + b.ResetTimer() + // assumes b.N < 2**24 + for i := 0; i < b.N; i++ { + arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} + addr := sdk.AccAddress(arr) + acc := mapper.NewAccountWithAddress(ctx, addr) + acc.SetCoins(coins) + mapper.SetAccount(ctx, acc) + } +} From a0a6bad4c03a572b905b4bd310874250b51df48b Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 2 Nov 2018 13:37:23 +0000 Subject: [PATCH 42/61] Fix test --- client/lcd/certificates_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/lcd/certificates_test.go b/client/lcd/certificates_test.go index 14bddfa0f8..3f48c194c2 100644 --- a/client/lcd/certificates_test.go +++ b/client/lcd/certificates_test.go @@ -17,7 +17,7 @@ func TestGenerateSelfSignedCert(t *testing.T) { cert, err := x509.ParseCertificate(certBytes) require.Nil(t, err) require.Equal(t, 2, len(cert.IPAddresses)) - require.Equal(t, 1, len(cert.DNSNames)) + require.Equal(t, 2, len(cert.DNSNames)) require.True(t, cert.IsCA) } From 36bb4409b6c3fc324e91fdd803b76d9ee3b0f382 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 2 Nov 2018 13:44:40 +0000 Subject: [PATCH 43/61] Refactor TrapSignal --- client/lcd/root.go | 24 +++++------------------- server/util.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/client/lcd/root.go b/client/lcd/root.go index 8b2fb4206c..13b3f7d26a 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -2,18 +2,13 @@ package lcd import ( "errors" - "net" - "net/http" - "os" - "os/signal" - "syscall" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" @@ -25,6 +20,9 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" tmserver "github.com/tendermint/tendermint/rpc/lib/server" + "net" + "net/http" + "os" ) const ( @@ -59,23 +57,11 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { var listener net.Listener var fingerprint string - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) closeListener := func() { err := listener.Close() logger.Error("error closing listener", "err", err) } - go func() { - sig := <-sigs - switch sig { - case syscall.SIGTERM: - defer closeListener() - os.Exit(128 + int(syscall.SIGTERM)) - case syscall.SIGINT: - defer closeListener() - os.Exit(128 + int(syscall.SIGINT)) - } - }() + server.TrapSignal(closeListener) if viper.GetBool(flagInsecure) { listener, err = tmserver.StartHTTPServer( diff --git a/server/util.go b/server/util.go index 5199c12071..51f10c765a 100644 --- a/server/util.go +++ b/server/util.go @@ -4,7 +4,9 @@ import ( "encoding/json" "net" "os" + "os/signal" "path/filepath" + "syscall" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -203,6 +205,23 @@ func ExternalIP() (string, error) { return "", errors.New("are you connected to the network?") } +// TrapSignal traps SIGINT and SIGTERM and terminates the server correctly. +func TrapSignal(cleanupFunc func()) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + sig := <-sigs + switch sig { + case syscall.SIGTERM: + defer cleanupFunc() + os.Exit(128 + int(syscall.SIGTERM)) + case syscall.SIGINT: + defer cleanupFunc() + os.Exit(128 + int(syscall.SIGINT)) + } + }() +} + func skipInterface(iface net.Interface) bool { if iface.Flags&net.FlagUp == 0 { return true // interface down From e598af9eb6489b872486bed6b4324419b7fb0315 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 2 Nov 2018 13:47:14 +0000 Subject: [PATCH 44/61] Fix lint --- client/lcd/root.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/lcd/root.go b/client/lcd/root.go index 13b3f7d26a..c6e7917e7b 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -52,17 +52,16 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { sslHosts := viper.GetString(flagSSLHosts) certFile := viper.GetString(flagSSLCertFile) keyFile := viper.GetString(flagSSLKeyFile) - cleanupFunc := func() {} var listener net.Listener var fingerprint string - closeListener := func() { + server.TrapSignal(func() { err := listener.Close() logger.Error("error closing listener", "err", err) - } - server.TrapSignal(closeListener) + }) + var cleanupFunc func() if viper.GetBool(flagInsecure) { listener, err = tmserver.StartHTTPServer( listenAddr, handler, logger, From 15c209324f56e99f1bf1b323a3057ec2d27fad8e Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sat, 3 Nov 2018 00:10:40 -0700 Subject: [PATCH 45/61] enforcing @jaekwon mergemaster --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dcdf7080b8..9d31a14f6a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers -* @ebuchman @rigelrozanski @cwgoes +* @jaekwon # Precious documentation /docs/ @zramsay @jolesbi From 50926ffffffddda3c67c279258c69202977706a2 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sun, 4 Nov 2018 21:28:38 -0500 Subject: [PATCH 46/61] Update to TM v0.26.0 - Part I (#2679) * Update to TM v0.26.0 --- CHANGELOG.md | 2 +- Gopkg.lock | 75 +++++++++-------------- Gopkg.toml | 28 +-------- baseapp/baseapp.go | 2 +- baseapp/baseapp_test.go | 16 ++--- client/context/context.go | 7 ++- client/context/query.go | 52 ++++++++-------- client/lcd/lcd_test.go | 4 +- client/tx/query.go | 2 +- client/utils/utils.go | 2 +- client/utils/utils_test.go | 4 +- cmd/gaia/app/app.go | 2 +- cmd/gaia/cmd/gaiadebug/main.go | 2 +- crypto/keys/keybase.go | 2 +- crypto/keys/types.go | 4 +- docs/sdk/core/app2.md | 2 +- docs/sdk/core/examples/app2.go | 2 +- docs/sdk/core/examples/app2_test.go | 4 +- examples/democoin/x/oracle/keeper.go | 4 +- examples/democoin/x/oracle/keeper_keys.go | 4 +- examples/democoin/x/oracle/oracle_test.go | 4 +- examples/democoin/x/simplestake/keeper.go | 4 +- server/start.go | 12 ++-- store/iavlstore.go | 35 +++++++---- store/iavlstore_test.go | 8 +-- store/list.go | 14 +++-- store/multistoreproof.go | 4 +- store/multistoreproof_test.go | 2 + store/queue.go | 4 +- store/queue_test.go | 6 +- store/rootmultistore.go | 39 ++++++------ store/rootmultistore_test.go | 4 +- types/decimal_test.go | 8 +-- x/auth/account_test.go | 6 +- x/auth/client/txbuilder/txbuilder.go | 5 +- x/auth/feekeeper.go | 4 +- x/auth/mapper.go | 4 +- x/auth/stdtx.go | 3 +- x/bank/client/cli/broadcast.go | 3 +- x/bank/client/rest/broadcast.go | 3 +- x/distribution/keeper/delegation.go | 4 +- x/distribution/keeper/genesis.go | 4 +- x/distribution/keeper/keeper.go | 8 +-- x/distribution/keeper/test_common.go | 2 +- x/distribution/keeper/validator.go | 4 +- x/gov/client/rest/rest.go | 2 +- x/gov/keeper.go | 30 ++++----- x/gov/keeper_test.go | 8 +-- x/gov/queryable.go | 4 +- x/gov/tally.go | 2 +- x/ibc/client/cli/relay.go | 6 +- x/ibc/mapper.go | 8 +-- x/mint/keeper.go | 4 +- x/slashing/client/cli/query.go | 2 +- x/slashing/client/rest/query.go | 2 +- x/slashing/keeper.go | 4 +- x/slashing/signing_info.go | 8 +-- x/slashing/slashing_period.go | 4 +- x/stake/handler.go | 4 +- x/stake/handler_test.go | 18 +++--- x/stake/keeper/delegation.go | 12 ++-- x/stake/keeper/keeper.go | 16 ++--- x/stake/keeper/val_state_change.go | 2 +- x/stake/keeper/validator.go | 8 +-- x/stake/types/delegation.go | 20 +++--- x/stake/types/params.go | 6 +- x/stake/types/pool.go | 6 +- x/stake/types/validator.go | 4 +- 68 files changed, 295 insertions(+), 299 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 764d481f88..16e01163cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -752,7 +752,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging) BREAKING CHANGES -* [stake] MarshalJSON -> MarshalBinary +* [stake] MarshalJSON -> MarshalBinaryLengthPrefixed * Queries against the store must be prefixed with the path "/store" FEATURES diff --git a/Gopkg.lock b/Gopkg.lock index 68350442d8..2a2aad42fc 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -10,6 +10,7 @@ revision = "48b08affede2cea076a3cf13b2e3f72ed262b743" [[projects]] + branch = "master" digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" packages = ["."] @@ -38,7 +39,7 @@ name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "2a560b2036bee5e3679ec2133eb6520b2f195213" + revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" [[projects]] digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" @@ -62,13 +63,6 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" -[[projects]] - digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b" - name = "github.com/ebuchman/fail-test" - packages = ["."] - pruneopts = "UT" - revision = "95f809107225be108efcf10a3509e4ea6ceef3c4" - [[projects]] digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" @@ -171,12 +165,13 @@ version = "v1.2.0" [[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", + "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -245,12 +240,12 @@ version = "v1.0.0" [[projects]] - digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3" + digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" name = "github.com/mitchellh/mapstructure" packages = ["."] pruneopts = "UT" - revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad" - version = "v1.1.1" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" [[projects]] digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" @@ -296,7 +291,7 @@ [[projects]] branch = "master" - digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" + digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -304,11 +299,11 @@ "model", ] pruneopts = "UT" - revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" + revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" [[projects]] branch = "master" - digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef" + digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" name = "github.com/prometheus/procfs" packages = [ ".", @@ -317,7 +312,7 @@ "xfs", ] pruneopts = "UT" - revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2" + revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" [[projects]] digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64" @@ -346,12 +341,12 @@ version = "v1.1.2" [[projects]] - digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" + digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" name = "github.com/spf13/cast" packages = ["."] pruneopts = "UT" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" + revision = "8c9545af88b134710ab1cd196795e7f2388358d7" + version = "v1.3.0" [[projects]] digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" @@ -370,12 +365,12 @@ version = "v1.0.0" [[projects]] - digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" + digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" name = "github.com/spf13/pflag" packages = ["."] pruneopts = "UT" - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" [[projects]] digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" @@ -424,35 +419,23 @@ revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" [[projects]] - branch = "master" - digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" - name = "github.com/tendermint/ed25519" - packages = [ - ".", - "edwards25519", - "extra25519", - ] - pruneopts = "UT" - revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" - -[[projects]] - digest = "1:2c971a45c89ca2ccc735af50919cdee05fbdc54d4bf50625073693300e31ead8" + digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "UT" - revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" - version = "v0.12.0" + revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12" + version = "v0.14.0" [[projects]] - digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430" + digest = "1:9f8c4c93658315a795ffd3e0c943d39f78067dd8382b8d7bcfaf6686b92f3978" name = "github.com/tendermint/iavl" packages = ["."] pruneopts = "UT" - revision = "3acc91fb8811db2c5409a855ae1f8e441fe98e2d" - version = "v0.11.0" + revision = "fa74114f764f9827c4ad5573f990ed25bf8c4bac" + version = "v0.11.1" [[projects]] - digest = "1:f9c7a1f3ee087476f4883c33cc7c1bdbe56b9670b2fb27855ea2f386393272f5" + digest = "1:aeef94796024739a6d2fb3df26450a1e3d9dbaeee0e7d896b180b1ae6a7e342e" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -485,6 +468,7 @@ "libs/db", "libs/errors", "libs/events", + "libs/fail", "libs/flowrate", "libs/log", "libs/pubsub", @@ -505,7 +489,6 @@ "rpc/core", "rpc/core/types", "rpc/grpc", - "rpc/lib", "rpc/lib/client", "rpc/lib/server", "rpc/lib/types", @@ -518,8 +501,8 @@ "version", ] pruneopts = "UT" - revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778" - version = "v0.25.1-rc0" + revision = "c086d0a34102bd42873d20445673ea1d18a539cd" + version = "v0.26.0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" @@ -530,13 +513,15 @@ version = "v0.1.0" [[projects]] - digest = "1:aaff04fa01d9b824fde6799759cc597b3ac3671b9ad31924c28b6557d0ee5284" + digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d" name = "golang.org/x/crypto" packages = [ "bcrypt", "blowfish", "chacha20poly1305", "curve25519", + "ed25519", + "ed25519/internal/edwards25519", "hkdf", "internal/chacha20", "internal/subtle", diff --git a/Gopkg.toml b/Gopkg.toml index 0902bc4126..85fa6603c7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,24 +1,3 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - [[constraint]] name = "github.com/bgentry/speakeasy" version = "~0.1.0" @@ -49,15 +28,15 @@ [[override]] name = "github.com/tendermint/go-amino" - version = "=v0.12.0" + version = "v0.14.0" [[override]] name = "github.com/tendermint/iavl" - version = "=v0.11.0" + version = "=v0.11.1" [[override]] name = "github.com/tendermint/tendermint" - version = "=0.25.1-rc0" + version = "v0.26.0" ## deps without releases: @@ -89,7 +68,6 @@ version = "1.0.0" ## transitive deps, without releases: -# [[override]] name = "github.com/syndtr/goleveldb" diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 17942b976a..a2b2f41667 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -337,7 +337,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc } // Encode with json - value := codec.Cdc.MustMarshalBinary(result) + value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) return abci.ResponseQuery{ Code: uint32(sdk.ABCICodeOK), Value: value, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index ff6cbb8341..9f6414214b 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -358,7 +358,7 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { if len(txBytes) == 0 { return nil, sdk.ErrTxDecode("txBytes are empty") } - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) } @@ -455,7 +455,7 @@ func TestCheckTx(t *testing.T) { for i := int64(0); i < nTxs; i++ { tx := newTxCounter(i, 0) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) r := app.CheckTx(txBytes) assert.True(t, r.IsOK(), fmt.Sprintf("%v", r)) @@ -503,7 +503,7 @@ func TestDeliverTx(t *testing.T) { for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -544,7 +544,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { { app.BeginBlock(abci.RequestBeginBlock{}) tx := newTxCounter(0, 0, 1, 2) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -565,7 +565,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { tx := newTxCounter(1, 3) tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{1}) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -638,7 +638,7 @@ func TestSimulateTx(t *testing.T) { require.Equal(t, gasConsumed, result.GasUsed) // simulate by calling Query with encoded tx - txBytes, err := cdc.MarshalBinary(tx) + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) require.Nil(t, err) query := abci.RequestQuery{ Path: "/app/simulate", @@ -648,7 +648,7 @@ func TestSimulateTx(t *testing.T) { require.True(t, queryResult.IsOK(), queryResult.Log) var res sdk.Result - codec.Cdc.MustUnmarshalBinary(queryResult.Value, &res) + codec.Cdc.MustUnmarshalBinaryLengthPrefixed(queryResult.Value, &res) require.Nil(t, err, "Result unmarshalling failed") require.True(t, res.IsOK(), res.Log) require.Equal(t, gasConsumed, res.GasUsed, res.Log) @@ -729,7 +729,7 @@ func TestRunInvalidTransaction(t *testing.T) { registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) - txBytes, err := newCdc.MarshalBinary(tx) + txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.EqualValues(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), res.Code) diff --git a/client/context/context.go b/client/context/context.go index ce36c37a8a..9108b3d0bb 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -121,8 +121,13 @@ func createVerifier() tmlite.Verifier { fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String()) os.Exit(1) } + node := rpcclient.NewHTTP(nodeURI, "/websocket") - verifier, err := tmliteProxy.NewVerifier(chainID, filepath.Join(home, ".gaialite"), node, log.NewNopLogger()) + cacheSize := 10 // TODO: determine appropriate cache size + verifier, err := tmliteProxy.NewVerifier( + chainID, filepath.Join(home, ".gaialite"), + node, log.NewNopLogger(), cacheSize, + ) if err != nil { fmt.Printf("Create verifier failed: %s\n", err.Error()) diff --git a/client/context/query.go b/client/context/query.go index 8fca9becff..06e24211a3 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,7 +10,6 @@ import ( "strings" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" @@ -54,7 +53,7 @@ func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sd return res, err } - ctx.Codec.MustUnmarshalBinary(resRaw, &res) + ctx.Codec.MustUnmarshalBinaryLengthPrefixed(resRaw, &res) return } @@ -157,8 +156,8 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro } opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Trusted: ctx.TrustNode, + Height: ctx.Height, + Prove: !ctx.TrustNode, } result, err := node.ABCIQueryWithOptions(path, key, opts) @@ -203,32 +202,33 @@ func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { return fmt.Errorf("missing valid certifier to verify data from distrusted node") } - // the AppHash for height H is in header H+1 - commit, err := ctx.Verify(resp.Height + 1) - if err != nil { - return err - } + // TODO: handle in another TM v0.26 update PR + // // the AppHash for height H is in header H+1 + // commit, err := ctx.Verify(resp.Height + 1) + // if err != nil { + // return err + // } - var multiStoreProof store.MultiStoreProof - cdc := codec.New() + // var multiStoreProof store.MultiStoreProof + // cdc := codec.New() - err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof) - if err != nil { - return errors.Wrap(err, "failed to unmarshalBinary rangeProof") - } + // err = cdc.UnmarshalBinaryLengthPrefixed(resp.Proof, &multiStoreProof) + // if err != nil { + // return errors.Wrap(err, "failed to unmarshalBinary rangeProof") + // } - // verify the substore commit hash against trusted appHash - substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( - multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, - ) - if err != nil { - return errors.Wrap(err, "failed in verifying the proof against appHash") - } + // // verify the substore commit hash against trusted appHash + // substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( + // multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, + // ) + // if err != nil { + // return errors.Wrap(err, "failed in verifying the proof against appHash") + // } - err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) - if err != nil { - return errors.Wrap(err, "failed in the range proof verification") - } + // err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) + // if err != nil { + // return errors.Wrap(err, "failed in the range proof verification") + // } return nil } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index cb25721e89..d3140e5b8d 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -158,11 +158,11 @@ func TestNodeStatus(t *testing.T) { res, body := Request(t, port, "GET", "/node_info", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var nodeInfo p2p.NodeInfo + var nodeInfo p2p.DefaultNodeInfo err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) require.Nil(t, err, "Couldn't parse node info") - require.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res) + require.NotEqual(t, p2p.DefaultNodeInfo{}, nodeInfo, "res: %v", res) // syncing res, body = Request(t, port, "GET", "/syncing", nil) diff --git a/client/tx/query.go b/client/tx/query.go index da94404f2d..3bbbf9a132 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -125,7 +125,7 @@ type Info struct { func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { var tx auth.StdTx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, err } diff --git a/client/utils/utils.go b/client/utils/utils.go index 9d20f0ab88..fee7c7fe25 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -167,7 +167,7 @@ func adjustGasEstimate(estimate int64, adjustment float64) int64 { func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) { var simulationResult sdk.Result - if err := cdc.UnmarshalBinary(rawRes, &simulationResult); err != nil { + if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil { return 0, err } return simulationResult.GasUsed, nil diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go index 731ded903f..c3bf315699 100644 --- a/client/utils/utils_test.go +++ b/client/utils/utils_test.go @@ -12,7 +12,7 @@ import ( func TestParseQueryResponse(t *testing.T) { cdc := app.MakeCodec() - sdkResBytes := cdc.MustMarshalBinary(sdk.Result{GasUsed: 10}) + sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10}) gas, err := parseQueryResponse(cdc, sdkResBytes) assert.Equal(t, gas, int64(10)) assert.Nil(t, err) @@ -28,7 +28,7 @@ func TestCalculateGas(t *testing.T) { if wantErr { return nil, errors.New("") } - return cdc.MustMarshalBinary(sdk.Result{GasUsed: gasUsed}), nil + return cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: gasUsed}), nil } } type args struct { diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 21ef855f02..87972dc37c 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -259,7 +259,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci if err != nil { panic(err) } - bz := app.cdc.MustMarshalBinary(tx) + bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx) res := app.BaseApp.DeliverTx(bz) if !res.IsOK() { panic(res.Log) diff --git a/cmd/gaia/cmd/gaiadebug/main.go b/cmd/gaia/cmd/gaiadebug/main.go index 96887376cd..240a74cb48 100644 --- a/cmd/gaia/cmd/gaiadebug/main.go +++ b/cmd/gaia/cmd/gaiadebug/main.go @@ -220,7 +220,7 @@ func runTxCmd(cmd *cobra.Command, args []string) error { var tx = auth.StdTx{} cdc := gaia.MakeCodec() - err = cdc.UnmarshalBinary(txBytes, &tx) + err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return err } diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 86ae5c1571..0bd500feef 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -273,7 +273,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t if err != nil { return nil, nil, err } - cdc.MustUnmarshalBinary([]byte(signed), sig) + cdc.MustUnmarshalBinaryLengthPrefixed([]byte(signed), sig) return sig, linfo.GetPubKey(), nil } sig, err = priv.Sign(msg) diff --git a/crypto/keys/types.go b/crypto/keys/types.go index ff90c3205d..eeb4fdfcf0 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -182,11 +182,11 @@ func (i offlineInfo) GetAddress() types.AccAddress { // encoding info func writeInfo(i Info) []byte { - return cdc.MustMarshalBinary(i) + return cdc.MustMarshalBinaryLengthPrefixed(i) } // decoding info func readInfo(bz []byte) (info Info, err error) { - err = cdc.UnmarshalBinary(bz, &info) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &info) return } diff --git a/docs/sdk/core/app2.md b/docs/sdk/core/app2.md index d8040a8500..b0e42fd10d 100644 --- a/docs/sdk/core/app2.md +++ b/docs/sdk/core/app2.md @@ -178,7 +178,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode(err.Error()) } diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index c1ae436668..24458384c0 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -201,7 +201,7 @@ func (tx app2Tx) GetSignature() []byte { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode(err.Error()) } diff --git a/docs/sdk/core/examples/app2_test.go b/docs/sdk/core/examples/app2_test.go index 9139034880..c59dec2647 100644 --- a/docs/sdk/core/examples/app2_test.go +++ b/docs/sdk/core/examples/app2_test.go @@ -39,7 +39,7 @@ func TestEncoding(t *testing.T) { cdc := NewCodec() testTxDecoder := tx2Decoder(cdc) - encodedSendTx, err := cdc.MarshalBinary(sendTxBefore) + encodedSendTx, err := cdc.MarshalBinaryLengthPrefixed(sendTxBefore) require.Nil(t, err, "Error encoding sendTx") @@ -69,7 +69,7 @@ func TestEncoding(t *testing.T) { Signature: sig, } - encodedIssueTx, err2 := cdc.MarshalBinary(issueTxBefore) + encodedIssueTx, err2 := cdc.MarshalBinaryLengthPrefixed(issueTxBefore) require.Nil(t, err2, "Error encoding issueTx") diff --git a/examples/democoin/x/oracle/keeper.go b/examples/democoin/x/oracle/keeper.go index e55cd7083c..d061d2f8ed 100644 --- a/examples/democoin/x/oracle/keeper.go +++ b/examples/democoin/x/oracle/keeper.go @@ -71,7 +71,7 @@ func (keeper Keeper) Info(ctx sdk.Context, p Payload) (res Info) { if bz == nil { return EmptyInfo(ctx) } - keeper.cdc.MustUnmarshalBinary(bz, &res) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } @@ -80,7 +80,7 @@ func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) { store := ctx.KVStore(keeper.key) key := GetInfoKey(p, keeper.cdc) - bz := keeper.cdc.MustMarshalBinary(info) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(info) store.Set(key, bz) } diff --git a/examples/democoin/x/oracle/keeper_keys.go b/examples/democoin/x/oracle/keeper_keys.go index 9b71aeaa18..d678692be9 100644 --- a/examples/democoin/x/oracle/keeper_keys.go +++ b/examples/democoin/x/oracle/keeper_keys.go @@ -7,13 +7,13 @@ import ( // GetInfoKey returns the key for OracleInfo func GetInfoKey(p Payload, cdc *codec.Codec) []byte { - bz := cdc.MustMarshalBinary(p) + bz := cdc.MustMarshalBinaryLengthPrefixed(p) return append([]byte{0x00}, bz...) } // GetSignPrefix returns the prefix for signs func GetSignPrefix(p Payload, cdc *codec.Codec) []byte { - bz := cdc.MustMarshalBinary(p) + bz := cdc.MustMarshalBinaryLengthPrefixed(p) return append([]byte{0x01}, bz...) } diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index 7621ea8609..1284f75b6d 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -82,7 +82,7 @@ func getSequence(ctx sdk.Context, key sdk.StoreKey) int { if seqbz == nil { seq = 0 } else { - codec.New().MustUnmarshalBinary(seqbz, &seq) + codec.New().MustUnmarshalBinaryLengthPrefixed(seqbz, &seq) } return seq @@ -96,7 +96,7 @@ func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error { return sdk.NewError(sdk.CodespaceRoot, 1, "") } - bz := codec.New().MustMarshalBinary(seq + 1) + bz := codec.New().MustMarshalBinaryLengthPrefixed(seq + 1) store.Set([]byte("seq"), bz) return nil diff --git a/examples/democoin/x/simplestake/keeper.go b/examples/democoin/x/simplestake/keeper.go index 7bdc17937b..607f61d83c 100644 --- a/examples/democoin/x/simplestake/keeper.go +++ b/examples/democoin/x/simplestake/keeper.go @@ -39,7 +39,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo { return bondInfo{} } var bi bondInfo - err := k.cdc.UnmarshalBinary(bz, &bi) + err := k.cdc.UnmarshalBinaryLengthPrefixed(bz, &bi) if err != nil { panic(err) } @@ -48,7 +48,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo { func (k Keeper) setBondInfo(ctx sdk.Context, addr sdk.AccAddress, bi bondInfo) { store := ctx.KVStore(k.key) - bz, err := k.cdc.MarshalBinary(bi) + bz, err := k.cdc.MarshalBinaryLengthPrefixed(bi) if err != nil { panic(err) } diff --git a/server/start.go b/server/start.go index 82fbbbbeba..cf39ff71b6 100644 --- a/server/start.go +++ b/server/start.go @@ -93,7 +93,6 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { return nil } -// nolint: unparam func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { cfg := ctx.Config home := cfg.RootDir @@ -135,7 +134,12 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } - // trap signal (run forever) - tmNode.RunForever() - return tmNode, nil + TrapSignal(func() { + if tmNode.IsRunning() { + _ = tmNode.Stop() + } + }) + + // run forever (the node will not be returned) + select {} } diff --git a/store/iavlstore.go b/store/iavlstore.go index d535fd436c..449058df56 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -5,7 +5,6 @@ import ( "io" "sync" - "github.com/tendermint/go-amino" "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" @@ -210,44 +209,56 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Height = getHeight(tree, req) switch req.Path { - case "/store", "/key": // Get by key + case "/key": key := req.Data // Data holds the key bytes + res.Key = key if !st.VersionExists(res.Height) { res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error() break } + if req.Prove { - value, proof, err := tree.GetVersionedWithProof(key, res.Height) + value, _, err := tree.GetVersionedWithProof(key, res.Height) if err != nil { res.Log = err.Error() break } + res.Value = value - cdc := amino.NewCodec() - p, err := cdc.MarshalBinary(proof) - if err != nil { - res.Log = err.Error() - break - } - res.Proof = p + // cdc := amino.NewCodec() + + // p, err := cdc.MarshalBinaryLengthPrefixed(proof) + // if err != nil { + // res.Log = err.Error() + // break + // } + + // TODO: handle in another TM v0.26 update PR + // res.Proof = p } else { _, res.Value = tree.GetVersioned(key, res.Height) } + case "/subspace": + var KVs []KVPair + subspace := req.Data res.Key = subspace - var KVs []KVPair + iterator := sdk.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) } + iterator.Close() - res.Value = cdc.MustMarshalBinary(KVs) + res.Value = cdc.MustMarshalBinaryLengthPrefixed(KVs) + default: msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) return sdk.ErrUnknownRequest(msg).QueryResult() } + return } diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 1e9263b7bb..d26cee055f 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -394,9 +394,9 @@ func TestIAVLStoreQuery(t *testing.T) { {Key: k1, Value: v3}, {Key: k2, Value: v2}, } - valExpSubEmpty := cdc.MustMarshalBinary(KVs0) - valExpSub1 := cdc.MustMarshalBinary(KVs1) - valExpSub2 := cdc.MustMarshalBinary(KVs2) + valExpSubEmpty := cdc.MustMarshalBinaryLengthPrefixed(KVs0) + valExpSub1 := cdc.MustMarshalBinaryLengthPrefixed(KVs1) + valExpSub2 := cdc.MustMarshalBinaryLengthPrefixed(KVs2) cid := iavlStore.Commit() ver := cid.Version @@ -459,7 +459,7 @@ func TestIAVLStoreQuery(t *testing.T) { require.Equal(t, valExpSub2, qres.Value) // default (height 0) will show latest -1 - query0 := abci.RequestQuery{Path: "/store", Data: k1} + query0 := abci.RequestQuery{Path: "/key", Data: k1} qres = iavlStore.Query(query0) require.Equal(t, uint32(sdk.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) diff --git a/store/list.go b/store/list.go index 9905b0d6f5..b38f11b803 100644 --- a/store/list.go +++ b/store/list.go @@ -42,21 +42,22 @@ func (m List) Len() (res uint64) { if bz == nil { return 0 } - m.cdc.MustUnmarshalBinary(bz, &res) + + m.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } // Get() returns the element by its index func (m List) Get(index uint64, ptr interface{}) error { bz := m.store.Get(ElemKey(index)) - return m.cdc.UnmarshalBinary(bz, ptr) + return m.cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) } // Set() stores the element to the given position // Setting element out of range will break length counting // Use Push() instead of Set() to append a new element func (m List) Set(index uint64, value interface{}) { - bz := m.cdc.MustMarshalBinary(value) + bz := m.cdc.MustMarshalBinaryLengthPrefixed(value) m.store.Set(ElemKey(index), bz) } @@ -72,7 +73,7 @@ func (m List) Delete(index uint64) { func (m List) Push(value interface{}) { length := m.Len() m.Set(length, value) - m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1)) + m.store.Set(LengthKey(), m.cdc.MustMarshalBinaryLengthPrefixed(length+1)) } // Iterate() is used to iterate over all existing elements in the list @@ -85,13 +86,16 @@ func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) for ; iter.Valid(); iter.Next() { v := iter.Value() - m.cdc.MustUnmarshalBinary(v, ptr) + m.cdc.MustUnmarshalBinaryLengthPrefixed(v, ptr) + k := iter.Key() s := string(k[len(k)-20:]) + index, err := strconv.ParseUint(s, 10, 64) if err != nil { panic(err) } + if fn(index) { break } diff --git a/store/multistoreproof.go b/store/multistoreproof.go index d62bc4aca5..74ddb71487 100644 --- a/store/multistoreproof.go +++ b/store/multistoreproof.go @@ -18,7 +18,7 @@ type MultiStoreProof struct { // buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte { var rangeProof iavl.RangeProof - cdc.MustUnmarshalBinary(iavlProof, &rangeProof) + cdc.MustUnmarshalBinaryLengthPrefixed(iavlProof, &rangeProof) msp := MultiStoreProof{ StoreInfos: storeInfos, @@ -26,7 +26,7 @@ func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []store RangeProof: rangeProof, } - proof := cdc.MustMarshalBinary(msp) + proof := cdc.MustMarshalBinaryLengthPrefixed(msp) return proof } diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index 45a102cd3b..a6fe95d2b7 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -12,6 +12,8 @@ import ( ) func TestVerifyMultiStoreCommitInfo(t *testing.T) { + // TODO: handle in another TM v0.26 update PR + t.SkipNow() appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") diff --git a/store/queue.go b/store/queue.go index 4081f63b3c..f41ecb7d7a 100644 --- a/store/queue.go +++ b/store/queue.go @@ -27,12 +27,12 @@ func (m Queue) getTop() (res uint64) { return 0 } - m.List.cdc.MustUnmarshalBinary(bz, &res) + m.List.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } func (m Queue) setTop(top uint64) { - bz := m.List.cdc.MustMarshalBinary(top) + bz := m.List.cdc.MustMarshalBinaryLengthPrefixed(top) m.List.store.Set(TopKey(), bz) } diff --git a/store/queue_test.go b/store/queue_test.go index 5ea6d906a4..58e96f56a1 100644 --- a/store/queue_test.go +++ b/store/queue_test.go @@ -81,7 +81,7 @@ func TestKeys(t *testing.T) { var actual int // Checking keys.LengthKey - err := cdc.UnmarshalBinary(store.Get(LengthKey()), &len) + err := cdc.UnmarshalBinaryLengthPrefixed(store.Get(LengthKey()), &len) require.Nil(t, err) require.Equal(t, len, queue.List.Len()) @@ -89,14 +89,14 @@ func TestKeys(t *testing.T) { for i := 0; i < 10; i++ { queue.List.Get(uint64(i), &expected) bz := store.Get(ElemKey(uint64(i))) - err = cdc.UnmarshalBinary(bz, &actual) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &actual) require.Nil(t, err) require.Equal(t, expected, actual) } queue.Pop() - err = cdc.UnmarshalBinary(store.Get(TopKey()), &top) + err = cdc.UnmarshalBinaryLengthPrefixed(store.Get(TopKey()), &top) require.Nil(t, err) require.Equal(t, top, queue.getTop()) } diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 7de465f770..f4801b1178 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,13 +295,13 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } - commitInfo, errMsg := getCommitInfo(rs.db, res.Height) - if errMsg != nil { - return sdk.ErrInternal(errMsg.Error()).QueryResult() - } - - res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) + // commitInfo, errMsg := getCommitInfo(rs.db, res.Height) + // if errMsg != nil { + // return sdk.ErrInternal(errMsg.Error()).QueryResult() + // } + // TODO: handle in another TM v0.26 update PR + // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) return res } @@ -386,11 +386,12 @@ type commitInfo struct { // Hash returns the simple merkle root hash of the stores sorted by name. func (ci commitInfo) Hash() []byte { - // TODO cache to ci.hash []byte - m := make(map[string]merkle.Hasher, len(ci.StoreInfos)) + // TODO: cache to ci.hash []byte + m := make(map[string][]byte, len(ci.StoreInfos)) for _, storeInfo := range ci.StoreInfos { - m[storeInfo.Name] = storeInfo + m[storeInfo.Name] = storeInfo.Hash() } + return merkle.SimpleHashFromMap(m) } @@ -422,13 +423,15 @@ type storeCore struct { func (si storeInfo) Hash() []byte { // Doesn't write Name, since merkle.SimpleHashFromMap() will // include them via the keys. - bz, _ := cdc.MarshalBinary(si.Core) // Does not error + bz, _ := cdc.MarshalBinaryLengthPrefixed(si.Core) hasher := tmhash.New() + _, err := hasher.Write(bz) if err != nil { // TODO: Handle with #870 panic(err) } + return hasher.Sum(nil) } @@ -441,16 +444,18 @@ func getLatestVersion(db dbm.DB) int64 { if latestBytes == nil { return 0 } - err := cdc.UnmarshalBinary(latestBytes, &latest) + + err := cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest) if err != nil { panic(err) } + return latest } // Set the latest version. func setLatestVersion(batch dbm.Batch, version int64) { - latestBytes, _ := cdc.MarshalBinary(version) // Does not error + latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version) batch.Set([]byte(latestVersionKey), latestBytes) } @@ -491,21 +496,19 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data") } - // Parse bytes. var cInfo commitInfo - err := cdc.UnmarshalBinary(cInfoBytes, &cInfo) + + err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) if err != nil { return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err) } + return cInfo, nil } // Set a commitInfo for given version. func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { - cInfoBytes, err := cdc.MarshalBinary(cInfo) - if err != nil { - panic(err) - } + cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo) cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) batch.Set([]byte(cInfoKey), cInfoBytes) } diff --git a/store/rootmultistore_test.go b/store/rootmultistore_test.go index d6a714c66e..501c5b730c 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmultistore_test.go @@ -215,7 +215,7 @@ func getExpectedCommitID(store *rootMultiStore, ver int64) CommitID { } func hashStores(stores map[StoreKey]CommitStore) []byte { - m := make(map[string]merkle.Hasher, len(stores)) + m := make(map[string][]byte, len(stores)) for key, store := range stores { name := key.Name() m[name] = storeInfo{ @@ -224,7 +224,7 @@ func hashStores(stores map[StoreKey]CommitStore) []byte { CommitID: store.LastCommitID(), // StoreType: store.GetStoreType(), }, - } + }.Hash() } return merkle.SimpleHashFromMap(m) } diff --git a/types/decimal_test.go b/types/decimal_test.go index 07329c7dc7..b358947713 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -305,11 +305,11 @@ func TestSerializationGocodecJSON(t *testing.T) { func TestSerializationGocodecBinary(t *testing.T) { d := mustNewDecFromStr(t, "0.333") - bz, err := cdc.MarshalBinary(d) + bz, err := cdc.MarshalBinaryLengthPrefixed(d) require.NoError(t, err) var d2 Dec - err = cdc.UnmarshalBinary(bz, &d2) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2) require.NoError(t, err) require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) } @@ -323,11 +323,11 @@ type testDEmbedStruct struct { // TODO make work for UnmarshalJSON func TestEmbeddedStructSerializationGocodec(t *testing.T) { obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} - bz, err := cdc.MarshalBinary(obj) + bz, err := cdc.MarshalBinaryLengthPrefixed(obj) require.Nil(t, err) var obj2 testDEmbedStruct - err = cdc.UnmarshalBinary(bz, &obj2) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2) require.Nil(t, err) require.Equal(t, obj.Field1, obj2.Field1) diff --git a/x/auth/account_test.go b/x/auth/account_test.go index b7a78e2d22..e48060fbef 100644 --- a/x/auth/account_test.go +++ b/x/auth/account_test.go @@ -93,16 +93,16 @@ func TestBaseAccountMarshal(t *testing.T) { cdc := codec.New() codec.RegisterCrypto(cdc) - b, err := cdc.MarshalBinary(acc) + b, err := cdc.MarshalBinaryLengthPrefixed(acc) require.Nil(t, err) acc2 := BaseAccount{} - err = cdc.UnmarshalBinary(b, &acc2) + err = cdc.UnmarshalBinaryLengthPrefixed(b, &acc2) require.Nil(t, err) require.Equal(t, acc, acc2) // error on bad bytes acc2 = BaseAccount{} - err = cdc.UnmarshalBinary(b[:len(b)/2], &acc2) + err = cdc.UnmarshalBinaryLengthPrefixed(b[:len(b)/2], &acc2) require.NotNil(t, err) } diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index b488ef359f..f516de4513 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -125,7 +125,8 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err if err != nil { return nil, err } - return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo)) + + return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo)) } // BuildAndSign builds a single message to be signed, and signs a transaction @@ -166,7 +167,7 @@ func (bldr TxBuilder) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, erro PubKey: info.GetPubKey(), }} - return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) + return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) } // SignStdTx appends a signature to a StdTx and returns a copy of a it. If append diff --git a/x/auth/feekeeper.go b/x/auth/feekeeper.go index a6be2e12de..0006a91852 100644 --- a/x/auth/feekeeper.go +++ b/x/auth/feekeeper.go @@ -36,12 +36,12 @@ func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins { } feePool := &(sdk.Coins{}) - fck.cdc.MustUnmarshalBinary(bz, feePool) + fck.cdc.MustUnmarshalBinaryLengthPrefixed(bz, feePool) return *feePool } func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) { - bz := fck.cdc.MustMarshalBinary(coins) + bz := fck.cdc.MustMarshalBinaryLengthPrefixed(coins) store := ctx.KVStore(fck.key) store.Set(collectedFeesKey, bz) } diff --git a/x/auth/mapper.go b/x/auth/mapper.go index dd127c7312..da5481749f 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -148,13 +148,13 @@ func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) int64 { if bz == nil { accNumber = 0 } else { - err := am.cdc.UnmarshalBinary(bz, &accNumber) + err := am.cdc.UnmarshalBinaryLengthPrefixed(bz, &accNumber) if err != nil { panic(err) } } - bz = am.cdc.MustMarshalBinary(accNumber + 1) + bz = am.cdc.MustMarshalBinaryLengthPrefixed(accNumber + 1) store.Set(globalAccountNumberKey, bz) return accNumber diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index ca6eb7b422..84bf88a4b7 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -151,10 +151,11 @@ func DefaultTxDecoder(cdc *codec.Codec) sdk.TxDecoder { // StdTx.Msg is an interface. The concrete types // are registered by MakeTxCodec - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) } + return tx, nil } } diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index e7e6bc16d1..dd045439e0 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -25,7 +25,8 @@ in place of an input filename, the command reads from standard input.`, if err != nil { return } - txBytes, err := cliCtx.Codec.MarshalBinary(stdTx) + + txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx) if err != nil { return } diff --git a/x/bank/client/rest/broadcast.go b/x/bank/client/rest/broadcast.go index c52961caf5..71696f12b3 100644 --- a/x/bank/client/rest/broadcast.go +++ b/x/bank/client/rest/broadcast.go @@ -22,11 +22,12 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht return } - txBytes, err := cliCtx.Codec.MarshalBinary(m.Tx) + txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } + res, err := cliCtx.BroadcastTx(txBytes) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 4dcc31a512..ac519107da 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -23,14 +23,14 @@ func (k Keeper) GetDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress, panic("Stored delegation-distribution info should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &ddi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &ddi) return } // set the delegator distribution info func (k Keeper) SetDelegationDistInfo(ctx sdk.Context, ddi types.DelegationDistInfo) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(ddi) + b := k.cdc.MustMarshalBinaryLengthPrefixed(ddi) store.Set(GetDelegationDistInfoKey(ddi.DelegatorAddr, ddi.ValOperatorAddr), b) } diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go index 06b153a51d..804ea52422 100644 --- a/x/distribution/keeper/genesis.go +++ b/x/distribution/keeper/genesis.go @@ -13,7 +13,7 @@ func (k Keeper) GetAllValidatorDistInfos(ctx sdk.Context) (vdis []types.Validato for ; iterator.Valid(); iterator.Next() { var vdi types.ValidatorDistInfo - k.cdc.MustUnmarshalBinary(iterator.Value(), &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vdi) vdis = append(vdis, vdi) } return vdis @@ -27,7 +27,7 @@ func (k Keeper) GetAllDelegationDistInfos(ctx sdk.Context) (ddis []types.Delegat for ; iterator.Valid(); iterator.Next() { var ddi types.DelegationDistInfo - k.cdc.MustUnmarshalBinary(iterator.Value(), &ddi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &ddi) ddis = append(ddis, ddi) } return ddis diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index d167e0efe2..a6fed96358 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -44,14 +44,14 @@ func (k Keeper) GetFeePool(ctx sdk.Context) (feePool types.FeePool) { if b == nil { panic("Stored fee pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &feePool) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &feePool) return } // set the global fee pool distribution info func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(feePool) + b := k.cdc.MustMarshalBinaryLengthPrefixed(feePool) store.Set(FeePoolKey, b) } @@ -77,14 +77,14 @@ func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) (consAddr sdk.ConsA panic("Previous proposer not set") } - k.cdc.MustUnmarshalBinary(b, &consAddr) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &consAddr) return } // get the proposer public key for this block func (k Keeper) SetPreviousProposerConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(consAddr) + b := k.cdc.MustMarshalBinaryLengthPrefixed(consAddr) store.Set(ProposerKey, b) } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index b1bd4ae4e0..0a02fb245f 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -172,7 +172,7 @@ func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, index := int64(0) for ; iter.Valid(); iter.Next() { var vdi types.ValidatorDistInfo - k.cdc.MustUnmarshalBinary(iter.Value(), &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vdi) if fn(index, vdi) { return } diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index d267d66b22..d2c755cfa6 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -23,14 +23,14 @@ func (k Keeper) GetValidatorDistInfo(ctx sdk.Context, panic("Stored validator-distribution info should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &vdi) return } // set the validator distribution info func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInfo) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(vdi) + b := k.cdc.MustMarshalBinaryLengthPrefixed(vdi) store.Set(GetValidatorDistInfoKey(vdi.OperatorAddr), b) } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 374f5cc2ac..842edfdacb 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -292,7 +292,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han var deposit gov.Deposit cdc.UnmarshalJSON(res, &deposit) if deposit.Empty() { - res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinaryLengthPrefixed(gov.QueryProposalParams{params.ProposalID})) if err != nil || len(res) == 0 { err := errors.Errorf("proposalID [%d] does not exist", proposalID) utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index d061f12062..a3e3cda253 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -106,7 +106,7 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID int64) Proposal { } var proposal Proposal - keeper.cdc.MustUnmarshalBinary(bz, &proposal) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal) return proposal } @@ -114,7 +114,7 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID int64) Proposal { // Implements sdk.AccountKeeper. func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposal) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal) store.Set(KeyProposal(proposal.GetProposalID()), bz) } @@ -175,7 +175,7 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk if bz != nil { return ErrInvalidGenesis(keeper.codespace, "Initial ProposalID already set") } - bz = keeper.cdc.MustMarshalBinary(proposalID) + bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID) store.Set(KeyNextProposalID, bz) return nil } @@ -197,8 +197,8 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd if bz == nil { return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) - bz = keeper.cdc.MustMarshalBinary(proposalID + 1) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) + bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID + 1) store.Set(KeyNextProposalID, bz) return proposalID, nil } @@ -210,7 +210,7 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e if bz == nil { return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) return proposalID, nil } @@ -298,13 +298,13 @@ func (keeper Keeper) GetVote(ctx sdk.Context, proposalID int64, voterAddr sdk.Ac return Vote{}, false } var vote Vote - keeper.cdc.MustUnmarshalBinary(bz, &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote) return vote, true } func (keeper Keeper) setVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress, vote Vote) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(vote) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote) store.Set(KeyVote(proposalID, voterAddr), bz) } @@ -330,13 +330,13 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID int64, depositerAddr return Deposit{}, false } var deposit Deposit - keeper.cdc.MustUnmarshalBinary(bz, &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &deposit) return deposit, true } func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID int64, depositerAddr sdk.AccAddress, deposit Deposit) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(deposit) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit) store.Set(KeyDeposit(proposalID, depositerAddr), bz) } @@ -398,7 +398,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID int64) { for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := &Deposit{} - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) _, _, err := keeper.ck.AddCoins(ctx, deposit.Depositer, deposit.Amount) if err != nil { @@ -434,14 +434,14 @@ func (keeper Keeper) getActiveProposalQueue(ctx sdk.Context) ProposalQueue { } var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) return proposalQueue } func (keeper Keeper) setActiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) store.Set(KeyActiveProposalQueue, bz) } @@ -480,14 +480,14 @@ func (keeper Keeper) getInactiveProposalQueue(ctx sdk.Context) ProposalQueue { var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) return proposalQueue } func (keeper Keeper) setInactiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) store.Set(KeyInactiveProposalQueue, bz) } diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 91c41d7d7d..0152c50bbd 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -122,11 +122,11 @@ func TestDeposits(t *testing.T) { // Test deposit iterator depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) require.Equal(t, addrs[0], deposit.Depositer) require.Equal(t, fourSteak.Plus(fiveSteak), deposit.Amount) depositsIterator.Next() - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) require.Equal(t, addrs[1], deposit.Depositer) require.Equal(t, fourSteak, deposit.Amount) depositsIterator.Next() @@ -184,14 +184,14 @@ func TestVotes(t *testing.T) { // Test vote iterator votesIterator := keeper.GetVotes(ctx, proposalID) require.True(t, votesIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) require.True(t, votesIterator.Valid()) require.Equal(t, addrs[0], vote.Voter) require.Equal(t, proposalID, vote.ProposalID) require.Equal(t, OptionYes, vote.Option) votesIterator.Next() require.True(t, votesIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) require.True(t, votesIterator.Valid()) require.Equal(t, addrs[1], vote.Voter) require.Equal(t, proposalID, vote.ProposalID) diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 93469a5ac2..ae7c6d9c55 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -126,7 +126,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := Deposit{} - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) deposits = append(deposits, deposit) } @@ -155,7 +155,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke votesIterator := keeper.GetVotes(ctx, params.ProposalID) for ; votesIterator.Valid(); votesIterator.Next() { vote := Vote{} - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) votes = append(votes, vote) } diff --git a/x/gov/tally.go b/x/gov/tally.go index b6e42c4b5a..2bfaa7dd55 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -39,7 +39,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { vote := &Vote{} - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), vote) // if validator, just record it in the map // if delegator tally voting power diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index ab7168aca3..43ad783f49 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -117,7 +117,7 @@ OUTER: var processed int64 if processedbz == nil { processed = 0 - } else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil { + } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(processedbz, &processed); err != nil { panic(err) } @@ -131,7 +131,7 @@ OUTER: var egressLength int64 if egressLengthbz == nil { egressLength = 0 - } else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil { + } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(egressLengthbz, &egressLength); err != nil { panic(err) } @@ -192,7 +192,7 @@ func (c relayCommander) getSequence(node string) int64 { func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte { var packet ibc.IBCPacket - if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil { + if err := c.cdc.UnmarshalBinaryLengthPrefixed(bz, &packet); err != nil { panic(err) } diff --git a/x/ibc/mapper.go b/x/ibc/mapper.go index 95c88b62fb..957ab191a8 100644 --- a/x/ibc/mapper.go +++ b/x/ibc/mapper.go @@ -33,13 +33,13 @@ func (ibcm Mapper) PostIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { // write everything into the state store := ctx.KVStore(ibcm.key) index := ibcm.getEgressLength(store, packet.DestChain) - bz, err := ibcm.cdc.MarshalBinary(packet) + bz, err := ibcm.cdc.MarshalBinaryLengthPrefixed(packet) if err != nil { panic(err) } store.Set(EgressKey(packet.DestChain, index), bz) - bz, err = ibcm.cdc.MarshalBinary(index + 1) + bz, err = ibcm.cdc.MarshalBinaryLengthPrefixed(index + 1) if err != nil { panic(err) } @@ -61,7 +61,7 @@ func (ibcm Mapper) ReceiveIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error // Functions for accessing the underlying KVStore. func marshalBinaryPanic(cdc *codec.Codec, value interface{}) []byte { - res, err := cdc.MarshalBinary(value) + res, err := cdc.MarshalBinaryLengthPrefixed(value) if err != nil { panic(err) } @@ -69,7 +69,7 @@ func marshalBinaryPanic(cdc *codec.Codec, value interface{}) []byte { } func unmarshalBinaryPanic(cdc *codec.Codec, bz []byte, ptr interface{}) { - err := cdc.UnmarshalBinary(bz, ptr) + err := cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) if err != nil { panic(err) } diff --git a/x/mint/keeper.go b/x/mint/keeper.go index 6e6a642f48..eba4c3fc18 100644 --- a/x/mint/keeper.go +++ b/x/mint/keeper.go @@ -59,14 +59,14 @@ func (k Keeper) GetMinter(ctx sdk.Context) (minter Minter) { if b == nil { panic("Stored fee pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &minter) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &minter) return } // set the minter func (k Keeper) SetMinter(ctx sdk.Context, minter Minter) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(minter) + b := k.cdc.MustMarshalBinaryLengthPrefixed(minter) store.Set(minterKey, b) } diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index 50ccc6c0ea..de4fc5d57d 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -34,7 +34,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { } signingInfo := new(slashing.ValidatorSigningInfo) - cdc.MustUnmarshalBinary(res, signingInfo) + cdc.MustUnmarshalBinaryLengthPrefixed(res, signingInfo) switch viper.Get(cli.OutputFlag) { diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index 99db0df63b..e55b6b9be7 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -45,7 +45,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code var signingInfo slashing.ValidatorSigningInfo - err = cdc.UnmarshalBinary(res, &signingInfo) + err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 647a9df098..718a024350 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -187,7 +187,7 @@ func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) { store := ctx.KVStore(k.storeKey) var pubkey crypto.PubKey - err := k.cdc.UnmarshalBinary(store.Get(getAddrPubkeyRelationKey(address)), &pubkey) + err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(getAddrPubkeyRelationKey(address)), &pubkey) if err != nil { return nil, fmt.Errorf("address %v not found", address) } @@ -196,7 +196,7 @@ func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKe func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(pubkey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(pubkey) store.Set(getAddrPubkeyRelationKey(addr), bz) } diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 8c35693f1c..6479be9285 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -15,7 +15,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress found = false return } - k.cdc.MustUnmarshalBinary(bz, &info) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &info) found = true return } @@ -23,7 +23,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress // Stored by *validator* address (not operator address) func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(info) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(info) store.Set(GetValidatorSigningInfoKey(address), bz) } @@ -36,14 +36,14 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con missed = false return } - k.cdc.MustUnmarshalBinary(bz, &missed) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) return } // Stored by *validator* address (not operator address) func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(missed) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed) store.Set(GetValidatorMissedBlockBitArrayKey(address, index), bz) } diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 4b13288588..026d141a2b 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -61,14 +61,14 @@ func (k Keeper) addOrUpdateValidatorSlashingPeriod(ctx sdk.Context, slashingPeri SlashedSoFar: slashingPeriod.SlashedSoFar, } store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(slashingPeriodValue) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(slashingPeriodValue) store.Set(GetValidatorSlashingPeriodKey(slashingPeriod.ValidatorAddr, slashingPeriod.StartHeight), bz) } // Unmarshal key/value into a ValidatorSlashingPeriod func (k Keeper) unmarshalSlashingPeriodKeyValue(key []byte, value []byte) ValidatorSlashingPeriod { var slashingPeriodValue ValidatorSlashingPeriodValue - k.cdc.MustUnmarshalBinary(value, &slashingPeriodValue) + k.cdc.MustUnmarshalBinaryLengthPrefixed(value, &slashingPeriodValue) address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen]) startHeight := int64(binary.BigEndian.Uint64(key[1+sdk.AddrLen:1+sdk.AddrLen+8]) - uint64(stake.ValidatorUpdateDelay)) return ValidatorSlashingPeriod{ diff --git a/x/stake/handler.go b/x/stake/handler.go index 1d095f3f4d..328a901330 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -201,7 +201,7 @@ func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k kee return err.Result() } - finishTime := types.MsgCdc.MustMarshalBinary(ubd.MinTime) + finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime) tags := sdk.NewTags( tags.Action, tags.ActionBeginUnbonding, @@ -219,7 +219,7 @@ func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k k return err.Result() } - finishTime := types.MsgCdc.MustMarshalBinary(red.MinTime) + finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(red.MinTime) tags := sdk.NewTags( tags.Action, tags.ActionBeginRedelegation, diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index c4a558b916..e5c78bcf6f 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -94,7 +94,7 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -232,7 +232,7 @@ func TestLegacyValidatorDelegations(t *testing.T) { require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -400,7 +400,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -494,7 +494,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -540,7 +540,7 @@ func TestMultipleMsgDelegate(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -570,7 +570,7 @@ func TestJailValidator(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error: %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -586,7 +586,7 @@ func TestJailValidator(t *testing.T) { msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper) require.True(t, got.IsOK(), "expected no error") - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -621,7 +621,7 @@ func TestValidatorQueue(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error: %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) origHeader := ctx.BlockHeader() @@ -709,7 +709,7 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) { // change the ctx to Block Time one second before the validator would have unbonded var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1)) // unbond the delegator from the validator diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 97acc24042..77f1323f2b 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -163,14 +163,14 @@ func (k Keeper) GetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time) if bz == nil { return []types.DVPair{} } - k.cdc.MustUnmarshalBinary(bz, &dvPairs) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs) return dvPairs } // Sets a specific unbonding queue timeslice. func (k Keeper) SetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetUnbondingDelegationTimeKey(timestamp), bz) } @@ -199,7 +199,7 @@ func (k Keeper) DequeueAllMatureUnbondingQueue(ctx sdk.Context, currTime time.Ti unbondingTimesliceIterator := k.UnbondingQueueIterator(ctx, ctx.BlockHeader().Time) for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() { timeslice := []types.DVPair{} - k.cdc.MustUnmarshalBinary(unbondingTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(unbondingTimesliceIterator.Value(), ×lice) matureUnbonds = append(matureUnbonds, timeslice...) store.Delete(unbondingTimesliceIterator.Key()) } @@ -300,14 +300,14 @@ func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Ti if bz == nil { return []types.DVVTriplet{} } - k.cdc.MustUnmarshalBinary(bz, &dvvTriplets) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets) return dvvTriplets } // Sets a specific redelegation queue timeslice. func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetRedelegationTimeKey(timestamp), bz) } @@ -336,7 +336,7 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time) for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { timeslice := []types.DVVTriplet{} - k.cdc.MustUnmarshalBinary(redelegationTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(redelegationTimesliceIterator.Value(), ×lice) matureRedelegations = append(matureRedelegations, timeslice...) store.Delete(redelegationTimesliceIterator.Key()) } diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 17ee31268c..b61ead54c9 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -60,14 +60,14 @@ func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) { if b == nil { panic("stored pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &pool) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &pool) return } // set the pool func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(pool) + b := k.cdc.MustMarshalBinaryLengthPrefixed(pool) store.Set(PoolKey, b) } @@ -80,14 +80,14 @@ func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Int) { if b == nil { return sdk.ZeroInt() } - k.cdc.MustUnmarshalBinary(b, &power) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &power) return } // Set the last total validator power. func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(power) + b := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(LastTotalPowerKey, b) } @@ -101,14 +101,14 @@ func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) if bz == nil { return sdk.ZeroInt() } - k.cdc.MustUnmarshalBinary(bz, &power) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) return } // Set the last validator power. func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(power) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(GetLastValidatorPowerKey(operator), bz) } @@ -128,13 +128,13 @@ func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 { return 0 } var counter int16 - k.cdc.MustUnmarshalBinary(b, &counter) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &counter) return counter } // set the current in-block validator operation counter func (k Keeper) SetIntraTxCounter(ctx sdk.Context, counter int16) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(counter) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(counter) store.Set(IntraTxCounterKey, bz) } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index ea057c0fdd..c259fe0254 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -73,7 +73,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // calculate the new power bytes newPower := validator.BondedTokens().RoundInt64() - newPowerBytes := k.cdc.MustMarshalBinary(sdk.NewInt(newPower)) + newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(sdk.NewInt(newPower)) // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 9fd7434d31..5770ada2ff 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -291,14 +291,14 @@ func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) if bz == nil { return []sdk.ValAddress{} } - k.cdc.MustUnmarshalBinary(bz, &valAddrs) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &valAddrs) return valAddrs } // Sets a specific validator queue timeslice. func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []sdk.ValAddress) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetValidatorQueueTimeKey(timestamp), bz) } @@ -325,7 +325,7 @@ func (k Keeper) GetAllMatureValidatorQueue(ctx sdk.Context, currTime time.Time) validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinary(validatorTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) matureValsAddrs = append(matureValsAddrs, timeslice...) } return matureValsAddrs @@ -337,7 +337,7 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinary(validatorTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) for _, valAddr := range timeslice { val, found := k.GetValidator(ctx, valAddr) if !found || val.GetStatus() != sdk.Unbonding { diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 88b67018d8..e731554274 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -47,7 +47,7 @@ func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { delegation.Shares, delegation.Height, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // return the delegation without fields contained within the key for the store @@ -62,7 +62,7 @@ func MustUnmarshalDelegation(cdc *codec.Codec, key, value []byte) Delegation { // return the delegation without fields contained within the key for the store func UnmarshalDelegation(cdc *codec.Codec, key, value []byte) (delegation Delegation, err error) { var storeValue delegationValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { err = fmt.Errorf("%v: %v", ErrNoDelegation(DefaultCodespace).Data(), err) return @@ -139,7 +139,7 @@ func MustMarshalUBD(cdc *codec.Codec, ubd UnbondingDelegation) []byte { ubd.InitialBalance, ubd.Balance, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a unbonding delegation from a store key and value @@ -154,7 +154,7 @@ func MustUnmarshalUBD(cdc *codec.Codec, key, value []byte) UnbondingDelegation { // unmarshal a unbonding delegation from a store key and value func UnmarshalUBD(cdc *codec.Codec, key, value []byte) (ubd UnbondingDelegation, err error) { var storeValue ubdValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } @@ -179,8 +179,8 @@ func UnmarshalUBD(cdc *codec.Codec, key, value []byte) (ubd UnbondingDelegation, // nolint func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool { - bz1 := MsgCdc.MustMarshalBinary(&d) - bz2 := MsgCdc.MustMarshalBinary(&d2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d2) return bytes.Equal(bz1, bz2) } @@ -231,7 +231,7 @@ func MustMarshalRED(cdc *codec.Codec, red Redelegation) []byte { red.SharesSrc, red.SharesDst, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a redelegation from a store key and value @@ -246,7 +246,7 @@ func MustUnmarshalRED(cdc *codec.Codec, key, value []byte) Redelegation { // unmarshal a redelegation from a store key and value func UnmarshalRED(cdc *codec.Codec, key, value []byte) (red Redelegation, err error) { var storeValue redValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } @@ -275,8 +275,8 @@ func UnmarshalRED(cdc *codec.Codec, key, value []byte) (red Redelegation, err er // nolint func (d Redelegation) Equal(d2 Redelegation) bool { - bz1 := MsgCdc.MustMarshalBinary(&d) - bz2 := MsgCdc.MustMarshalBinary(&d2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d2) return bytes.Equal(bz1, bz2) } diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 5915570c12..699758ace4 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -49,8 +49,8 @@ func (p *Params) KeyValuePairs() params.KeyValuePairs { // Equal returns a boolean determining if two Param types are identical. func (p Params) Equal(p2 Params) bool { - bz1 := MsgCdc.MustMarshalBinary(&p) - bz2 := MsgCdc.MustMarshalBinary(&p2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p2) return bytes.Equal(bz1, bz2) } @@ -85,7 +85,7 @@ func MustUnmarshalParams(cdc *codec.Codec, value []byte) Params { // unmarshal the current staking params value from store key func UnmarshalParams(cdc *codec.Codec, value []byte) (params Params, err error) { - err = cdc.UnmarshalBinary(value, ¶ms) + err = cdc.UnmarshalBinaryLengthPrefixed(value, ¶ms) if err != nil { return } diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index e2015dcafc..4b227aa805 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -16,8 +16,8 @@ type Pool struct { // nolint func (p Pool) Equal(p2 Pool) bool { - bz1 := MsgCdc.MustMarshalBinary(&p) - bz2 := MsgCdc.MustMarshalBinary(&p2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p2) return bytes.Equal(bz1, bz2) } @@ -90,7 +90,7 @@ func MustUnmarshalPool(cdc *codec.Codec, value []byte) Pool { // unmarshal the current pool value from store key func UnmarshalPool(cdc *codec.Codec, value []byte) (pool Pool, err error) { - err = cdc.UnmarshalBinary(value, &pool) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &pool) if err != nil { return } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 52d30f0a31..9301da3ece 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -87,7 +87,7 @@ func MustMarshalValidator(cdc *codec.Codec, validator Validator) []byte { UnbondingMinTime: validator.UnbondingMinTime, Commission: validator.Commission, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a redelegation from a store key and value @@ -106,7 +106,7 @@ func UnmarshalValidator(cdc *codec.Codec, operatorAddr, value []byte) (validator return } var storeValue validatorValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } From 5b74e1d0b63404d31455c4beb709d3dea2c261d1 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 19:36:35 -0800 Subject: [PATCH 47/61] Bez/tm0.26 update pt 2 redux (#2684) * Update to TM v0.26.0 * Update TODOs * Proof and verification updates * Fix linting * Fix key path creation * Temporarily fix tendermint revision to make tests pass --- Gopkg.lock | 5 +- Gopkg.toml | 2 +- client/context/query.go | 75 ++++++++----- store/iavlstore.go | 17 +-- store/multistoreproof.go | 181 +++++++++++++++++++------------ store/multistoreproof_test.go | 197 ++++++++++++++++------------------ store/rootmultistore.go | 17 ++- 7 files changed, 271 insertions(+), 223 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 2a2aad42fc..f9be7bd97a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -435,7 +435,7 @@ version = "v0.11.1" [[projects]] - digest = "1:aeef94796024739a6d2fb3df26450a1e3d9dbaeee0e7d896b180b1ae6a7e342e" + digest = "1:92d7d1678577fd1a6f3348168cef87880bbc710ef5f4e9a1216f45c56567d734" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -501,8 +501,7 @@ "version", ] pruneopts = "UT" - revision = "c086d0a34102bd42873d20445673ea1d18a539cd" - version = "v0.26.0" + revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index 85fa6603c7..a66574ea62 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -36,7 +36,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "v0.26.0" + revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" # TODO replace w/ 0.26.1 ## deps without releases: diff --git a/client/context/query.go b/client/context/query.go index 06e24211a3..572a137787 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" @@ -197,38 +198,34 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) { } // verifyProof perform response proof verification. -func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { +func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) error { if ctx.Verifier == nil { return fmt.Errorf("missing valid certifier to verify data from distrusted node") } - // TODO: handle in another TM v0.26 update PR - // // the AppHash for height H is in header H+1 - // commit, err := ctx.Verify(resp.Height + 1) - // if err != nil { - // return err - // } + // the AppHash for height H is in header H+1 + commit, err := ctx.Verify(resp.Height + 1) + if err != nil { + return err + } - // var multiStoreProof store.MultiStoreProof - // cdc := codec.New() + // TODO: Instead of reconstructing, stash on CLIContext field? + prt := store.DefaultProofRuntime() - // err = cdc.UnmarshalBinaryLengthPrefixed(resp.Proof, &multiStoreProof) - // if err != nil { - // return errors.Wrap(err, "failed to unmarshalBinary rangeProof") - // } + // TODO: Better convention for path? + storeName, err := parseQueryStorePath(queryPath) + if err != nil { + return err + } - // // verify the substore commit hash against trusted appHash - // substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( - // multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, - // ) - // if err != nil { - // return errors.Wrap(err, "failed in verifying the proof against appHash") - // } + kp := merkle.KeyPath{} + kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) + kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) - // err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) - // if err != nil { - // return errors.Wrap(err, "failed in the range proof verification") - // } + err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value) + if err != nil { + return errors.Wrap(err, "failed to prove merkle proof") + } return nil } @@ -241,20 +238,40 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([ } // isQueryStoreWithProof expects a format like /// -// queryType can be app or store. +// queryType must be "store" and subpath must be "key" to require a proof. func isQueryStoreWithProof(path string) bool { if !strings.HasPrefix(path, "/") { return false } paths := strings.SplitN(path[1:], "/", 3) - if len(paths) != 3 { + switch { + case len(paths) != 3: return false - } - - if store.RequireProof("/" + paths[2]) { + case paths[0] != "store": + return false + case store.RequireProof("/" + paths[2]): return true } return false } + +// parseQueryStorePath expects a format like /store//key. +func parseQueryStorePath(path string) (storeName string, err error) { + if !strings.HasPrefix(path, "/") { + return "", errors.New("expected path to start with /") + } + + paths := strings.SplitN(path[1:], "/", 3) + switch { + case len(paths) != 3: + return "", errors.New("expected format like /store//key") + case paths[0] != "store": + return "", errors.New("expected format like /store//key") + case paths[2] != "key": + return "", errors.New("expected format like /store//key") + } + + return paths[1], nil +} diff --git a/store/iavlstore.go b/store/iavlstore.go index 449058df56..3b127357d7 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -7,6 +7,7 @@ import ( "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" @@ -209,8 +210,8 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Height = getHeight(tree, req) switch req.Path { - case "/key": - key := req.Data // Data holds the key bytes + case "/key": // get by key + key := req.Data // data holds the key bytes res.Key = key if !st.VersionExists(res.Height) { @@ -224,18 +225,8 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Log = err.Error() break } - res.Value = value - // cdc := amino.NewCodec() - - // p, err := cdc.MarshalBinaryLengthPrefixed(proof) - // if err != nil { - // res.Log = err.Error() - // break - // } - - // TODO: handle in another TM v0.26 update PR - // res.Proof = p + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} } else { _, res.Value = tree.GetVersioned(key, res.Height) } diff --git a/store/multistoreproof.go b/store/multistoreproof.go index 74ddb71487..96f0a48373 100644 --- a/store/multistoreproof.go +++ b/store/multistoreproof.go @@ -2,90 +2,139 @@ package store import ( "bytes" + "fmt" - "github.com/pkg/errors" "github.com/tendermint/iavl" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" ) // MultiStoreProof defines a collection of store proofs in a multi-store type MultiStoreProof struct { StoreInfos []storeInfo - StoreName string - RangeProof iavl.RangeProof } -// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos -func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte { - var rangeProof iavl.RangeProof - cdc.MustUnmarshalBinaryLengthPrefixed(iavlProof, &rangeProof) - - msp := MultiStoreProof{ - StoreInfos: storeInfos, - StoreName: storeName, - RangeProof: rangeProof, - } - - proof := cdc.MustMarshalBinaryLengthPrefixed(msp) - return proof +func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof { + return &MultiStoreProof{StoreInfos: storeInfos} } -// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash -func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) { - var substoreCommitHash []byte - var height int64 - for _, storeInfo := range storeInfos { - if storeInfo.Name == storeName { - substoreCommitHash = storeInfo.Core.CommitID.Hash - height = storeInfo.Core.CommitID.Version - } - } - if len(substoreCommitHash) == 0 { - return nil, cmn.NewError("failed to get substore root commit hash by store name") - } - +// ComputeRootHash returns the root hash for a given multi-store proof. +func (proof *MultiStoreProof) ComputeRootHash() []byte { ci := commitInfo{ - Version: height, - StoreInfos: storeInfos, + Version: -1, // TODO: Not needed; improve code. + StoreInfos: proof.StoreInfos, } - if !bytes.Equal(appHash, ci.Hash()) { - return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash") - } - return substoreCommitHash, nil + return ci.Hash() } -// VerifyRangeProof verify iavl RangeProof -func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error { - - // verify the proof to ensure data integrity. - err := rangeProof.Verify(substoreCommitHash) - if err != nil { - return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash") - } - - if len(value) != 0 { - // verify existence proof - err = rangeProof.VerifyItem(key, value) - if err != nil { - return errors.Wrap(err, "failed in existence verification") - } - } else { - // verify absence proof - err = rangeProof.VerifyAbsence(key) - if err != nil { - return errors.Wrap(err, "failed in absence verification") - } - } - - return nil -} - -// RequireProof return whether proof is require for the subpath +// RequireProof returns whether proof is required for the subpath. func RequireProof(subpath string) bool { - // Currently, only when query subpath is "/store" or "/key", will proof be included in response. - // If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212 - if subpath == "/store" || subpath == "/key" { + // XXX: create a better convention. + // Currently, only when query subpath is "/key", will proof be included in + // response. If there are some changes about proof building in iavlstore.go, + // we must change code here to keep consistency with iavlStore#Query. + if subpath == "/key" { return true } + return false } + +//----------------------------------------------------------------------------- + +var _ merkle.ProofOperator = MultiStoreProofOp{} + +// the multi-store proof operation constant value +const ProofOpMultiStore = "multistore" + +// TODO: document +type MultiStoreProofOp struct { + // Encoded in ProofOp.Key + key []byte + + // To encode in ProofOp.Data. + Proof *MultiStoreProof `json:"proof"` +} + +func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp { + return MultiStoreProofOp{ + key: key, + Proof: proof, + } +} + +// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a +// given proof operation. +func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) { + if pop.Type != ProofOpMultiStore { + return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore) + } + + // XXX: a bit strange as we'll discard this, but it works + var op MultiStoreProofOp + + err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op) + if err != nil { + return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into MultiStoreProofOp") + } + + return NewMultiStoreProofOp(pop.Key, op.Proof), nil +} + +// ProofOp return a merkle proof operation from a given multi-store proof +// operation. +func (op MultiStoreProofOp) ProofOp() merkle.ProofOp { + bz := cdc.MustMarshalBinaryLengthPrefixed(op) + return merkle.ProofOp{ + Type: ProofOpMultiStore, + Key: op.key, + Data: bz, + } +} + +// String implements the Stringer interface for a mult-store proof operation. +func (op MultiStoreProofOp) String() string { + return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey()) +} + +// GetKey returns the key for a multi-store proof operation. +func (op MultiStoreProofOp) GetKey() []byte { + return op.key +} + +// Run executes a multi-store proof operation for a given value. It returns +// the root hash if the value matches all the store's commitID's hash or an +// error otherwise. +func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) { + if len(args) != 1 { + return nil, cmn.NewError("Value size is not 1") + } + + value := args[0] + root := op.Proof.ComputeRootHash() + + for _, si := range op.Proof.StoreInfos { + if si.Name == string(op.key) { + if bytes.Equal(value, si.Core.CommitID.Hash) { + return [][]byte{root}, nil + } + + return nil, cmn.NewError("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value) + } + } + + return nil, cmn.NewError("key %v not found in multistore proof", op.key) +} + +//----------------------------------------------------------------------------- + +// XXX: This should be managed by the rootMultiStore which may want to register +// more proof ops? +func DefaultProofRuntime() (prt *merkle.ProofRuntime) { + prt = merkle.NewProofRuntime() + prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) + prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) + return +} diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index a6fe95d2b7..19feef1600 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -1,125 +1,108 @@ package store import ( - "encoding/hex" "testing" - "github.com/stretchr/testify/assert" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/db" + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" ) -func TestVerifyMultiStoreCommitInfo(t *testing.T) { - // TODO: handle in another TM v0.26 update PR - t.SkipNow() - appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") - - substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") - storeName := "acc" - - var storeInfos []storeInfo - - gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78") - storeInfos = append(storeInfos, storeInfo{ - Name: "gov", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: gocRootHash, - }, - }, - }) - - storeInfos = append(storeInfos, storeInfo{ - Name: "main", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: nil, - }, - }, - }) - - accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") - storeInfos = append(storeInfos, storeInfo{ - Name: "acc", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: accRootHash, - }, - }, - }) - - storeInfos = append(storeInfos, storeInfo{ - Name: "ibc", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: nil, - }, - }, - }) - - stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4") - storeInfos = append(storeInfos, storeInfo{ - Name: "stake", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: stakeRootHash, - }, - }, - }) - - slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e") - storeInfos = append(storeInfos, storeInfo{ - Name: "slashing", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: slashingRootHash, - }, - }, - }) - - commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) +func TestVerifyIAVLStoreQueryProof(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + iStore, err := LoadIAVLStore(db, CommitID{}, sdk.PruneNothing) + store := iStore.(*iavlStore) require.Nil(t, err) - require.Equal(t, commitHash, substoreRootHash) + store.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() - appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3") + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) - _, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) - require.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo") + // Verify proof. + prt := DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY_NOT", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE_NOT")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte(nil)) + require.NotNil(t, err) } -func TestVerifyRangeProof(t *testing.T) { - tree := iavl.NewMutableTree(db.NewMemDB(), 0) +func TestVerifyMultiStoreQueryProof(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") - rand := cmn.NewRand() - rand.Seed(0) // for determinism - for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} { - key := []byte{ikey} - tree.Set(key, []byte(rand.Str(8))) - } + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) - root := tree.WorkingHash() + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() - key := []byte{0x32} - val, proof, err := tree.GetWithProof(key) - assert.Nil(t, err) - assert.NotEmpty(t, val) - assert.NotEmpty(t, proof) - err = VerifyRangeProof(key, val, root, proof) - assert.Nil(t, err) + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) - key = []byte{0x40} - val, proof, err = tree.GetWithProof(key) - assert.Nil(t, err) - assert.Empty(t, val) - assert.NotEmpty(t, proof) - err = VerifyRangeProof(key, val, root, proof) - assert.Nil(t, err) + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY_NOT", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE_NOT")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) + require.NotNil(t, err) } diff --git a/store/rootmultistore.go b/store/rootmultistore.go index f4801b1178..6c491bda13 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,10 +295,16 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } - // commitInfo, errMsg := getCommitInfo(rs.db, res.Height) - // if errMsg != nil { - // return sdk.ErrInternal(errMsg.Error()).QueryResult() - // } + commitInfo, errMsg := getCommitInfo(rs.db, res.Height) + if errMsg != nil { + return sdk.ErrInternal(errMsg.Error()).QueryResult() + } + + // Restore origin path and append proof op. + res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp( + []byte(storeName), + NewMultiStoreProof(commitInfo.StoreInfos), + ).ProofOp()) // TODO: handle in another TM v0.26 update PR // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) @@ -313,11 +319,14 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) { err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) return } + paths := strings.SplitN(path[1:], "/", 2) storeName = paths[0] + if len(paths) == 2 { subpath = "/" + paths[1] } + return } From f4338d6f756d15c0fe55e6e61b6867a5d5e3b354 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 19:42:11 -0800 Subject: [PATCH 48/61] Fix merge conflict bug; Update PENDING --- PENDING.md | 2 +- store/iavlstore.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index 84a08a5abe..2c3c3cd1d9 100644 --- a/PENDING.md +++ b/PENDING.md @@ -12,7 +12,7 @@ BREAKING CHANGES * [simulation] \#2665 only argument to simulation.Invariant is now app * Tendermint - + * Upgrade to version 0.26.0 FEATURES diff --git a/store/iavlstore.go b/store/iavlstore.go index 3b127357d7..bb588f029b 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -220,7 +220,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } if req.Prove { - value, _, err := tree.GetVersionedWithProof(key, res.Height) + value, proof, err := tree.GetVersionedWithProof(key, res.Height) if err != nil { res.Log = err.Error() break From c20fcbfd8f6f3c4be406f10d38d3eb1bfcdd4ced Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 5 Nov 2018 04:26:46 +0000 Subject: [PATCH 49/61] New genesis workflow (#2602) New genesis workflow: * `gaiad init` is now used to generate an empty `genesis.json`. * Genesis accounts need to be populated manually before running `gaiad collect-gentxs`. * This should support starfish too, see #2615 for more info. * Closes: #2596 #2615 * Validate validator address and address against respective account ex ante * Fix local testnet failures * New genesis tests * Run make format * Add --pubkey flag * gaiad collect-gentxs takes no args --- PENDING.md | 1 + client/lcd/test_helpers.go | 11 +- cmd/gaia/app/app.go | 3 +- cmd/gaia/app/genesis.go | 166 ++++++++++--------- cmd/gaia/app/genesis_test.go | 44 ++++-- cmd/gaia/cli_test/cli_test.go | 41 ++++- cmd/gaia/cmd/gaiad/main.go | 3 +- cmd/gaia/init/collect.go | 119 ++++++++++++++ cmd/gaia/init/gentx.go | 44 ++++-- cmd/gaia/init/init.go | 201 +++++------------------- cmd/gaia/init/init_test.go | 1 - cmd/gaia/init/testnet.go | 67 ++++++-- examples/basecoin/cmd/basecoind/main.go | 13 +- examples/democoin/cmd/democoind/main.go | 17 +- server/init.go | 9 +- server/mock/app.go | 7 +- server/mock/app_test.go | 3 +- x/stake/client/cli/flags.go | 16 +- x/stake/client/cli/tx.go | 8 +- 19 files changed, 463 insertions(+), 311 deletions(-) create mode 100644 cmd/gaia/init/collect.go diff --git a/PENDING.md b/PENDING.md index 2c3c3cd1d9..9acbf325b7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -7,6 +7,7 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * Gaia + * [gaiad init] \#2602 New genesis workflow * SDK * [simulation] \#2665 only argument to simulation.Invariant is now app diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 2a81239ac6..39c76865cc 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -214,6 +214,7 @@ func InitializeTestLCD( genTxs := []json.RawMessage{} // append any additional (non-proposing) validators + var accs []gapp.GenesisAccount for i := 0; i < nValidators; i++ { operPrivKey := secp256k1.GenPrivKey() operAddr := operPrivKey.PubKey().Address() @@ -242,9 +243,17 @@ func InitializeTestLCD( genTxs = append(genTxs, txBytes) valConsPubKeys = append(valConsPubKeys, pubKey) valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) + + accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(operAddr)) + accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 150)} + accs = append(accs, gapp.NewGenesisAccount(&accAuth)) } - genesisState, err := gapp.GaiaAppGenState(cdc, genTxs) + appGenState := gapp.NewDefaultGenesisState() + appGenState.Accounts = accs + genDoc.AppState, err = cdc.MarshalJSON(appGenState) + require.NoError(t, err) + genesisState, err := gapp.GaiaAppGenState(cdc, *genDoc, genTxs) require.NoError(t, err) // add some tokens to init accounts diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 87972dc37c..5f1f61f1e4 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -273,7 +273,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // sanity check if len(req.Validators) > 0 { if len(req.Validators) != len(validators) { - panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d) ", len(req.Validators), len(validators))) + panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d)", + len(req.Validators), len(validators))) } sort.Sort(abci.ValidatorUpdates(req.Validators)) sort.Sort(abci.ValidatorUpdates(validators)) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index df28bcf6c1..cfbbe5cc2e 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -26,6 +26,7 @@ var ( // bonded tokens given to genesis validators/accounts freeFermionVal = int64(100) freeFermionsAcc = sdk.NewInt(150) + bondDenom = "steak" ) // State to Unmarshal @@ -90,58 +91,60 @@ func GaiaAppInit() server.AppInit { // Create the core parameters for genesis initialization for gaia // note that the pubkey input is this machines pubkey -func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) { - if len(appGenTxs) == 0 { - err = errors.New("must provide at least genesis transaction") - return +func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + genesisState GenesisState, err error) { + + if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { + return genesisState, err } - // start with the default staking genesis state - stakeData := stake.DefaultGenesisState() - slashingData := slashing.DefaultGenesisState() - - // get genesis flag account information - genaccs := make([]GenesisAccount, len(appGenTxs)) + // if there are no gen txs to be processed, return the default empty state + if len(appGenTxs) == 0 { + return genesisState, errors.New("there must be at least one genesis tx") + } + stakeData := genesisState.StakeData for i, genTx := range appGenTxs { var tx auth.StdTx - err = cdc.UnmarshalJSON(genTx, &tx) - if err != nil { - return + if err := cdc.UnmarshalJSON(genTx, &tx); err != nil { + return genesisState, err } msgs := tx.GetMsgs() if len(msgs) != 1 { - err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message") - return + return genesisState, errors.New( + "must provide genesis StdTx with exactly 1 CreateValidator message") + } + if _, ok := msgs[0].(stake.MsgCreateValidator); !ok { + return genesisState, fmt.Errorf( + "Genesis transaction %v does not contain a MsgCreateValidator", i) } - msg := msgs[0].(stake.MsgCreateValidator) - - // create the genesis account, give'm few steaks and a buncha token with there name - genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply } - // create the final app state - genesisState = GenesisState{ - Accounts: genaccs, - StakeData: stakeData, + for _, acc := range genesisState.Accounts { + // create the genesis account, give'm few steaks and a buncha token with there name + for _, coin := range acc.Coins { + if coin.Denom == bondDenom { + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens. + Add(sdk.NewDecFromInt(coin.Amount)) // increase the supply + } + } + } + genesisState.StakeData = stakeData + genesisState.GenTxs = appGenTxs + return genesisState, nil +} + +// NewDefaultGenesisState generates the default state for gaia. +func NewDefaultGenesisState() GenesisState { + return GenesisState{ + Accounts: nil, + StakeData: stake.DefaultGenesisState(), MintData: mint.DefaultGenesisState(), DistrData: distr.DefaultGenesisState(), GovData: gov.DefaultGenesisState(), - SlashingData: slashingData, - GenTxs: appGenTxs, + SlashingData: slashing.DefaultGenesisState(), + GenTxs: nil, } - - return -} - -func genesisAccountFromMsgCreateValidator(msg stake.MsgCreateValidator, amount sdk.Int) GenesisAccount { - accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr)) - accAuth.Coins = []sdk.Coin{ - {msg.Description.Moniker + "Token", sdk.NewInt(1000)}, - {"steak", amount}, - } - return NewGenesisAccount(&accAuth) } // GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants @@ -175,27 +178,43 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) { } // GaiaAppGenState but with JSON -func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func GaiaAppGenStateJSON(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { // create the final app state - genesisState, err := GaiaAppGenState(cdc, appGenTxs) + genesisState, err := GaiaAppGenState(cdc, genDoc, appGenTxs) if err != nil { return nil, err } - appState, err = codec.MarshalJSONIndent(cdc, genesisState) - return + return codec.MarshalJSONIndent(cdc, genesisState) } -// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators, -// appGenTxs, and persistent peers required to generate genesis.json. -func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( - validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) { +// CollectStdTxs processes and validates application's genesis StdTxs and returns +// the list of appGenTxs, and persistent peers required to generate genesis.json. +func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) ( + appGenTxs []auth.StdTx, persistentPeers string, err error) { + var fos []os.FileInfo fos, err = ioutil.ReadDir(genTxsDir) if err != nil { - return + return appGenTxs, persistentPeers, err } - var addresses []string + // prepare a map of all accounts in genesis state to then validate + // against the validators addresses + var appState GenesisState + if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { + return appGenTxs, persistentPeers, err + } + addrMap := make(map[string]GenesisAccount, len(appState.Accounts)) + for i := 0; i < len(appState.Accounts); i++ { + acc := appState.Accounts[i] + strAddr := string(acc.Address) + addrMap[strAddr] = acc + } + + // addresses and IPs (and port) validator server info + var addressesIPs []string + for _, fo := range fos { filename := filepath.Join(genTxsDir, fo.Name()) if !fo.IsDir() && (filepath.Ext(filename) != ".json") { @@ -204,48 +223,55 @@ func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( // get the genStdTx var jsonRawTx []byte - jsonRawTx, err = ioutil.ReadFile(filename) - if err != nil { - return + if jsonRawTx, err = ioutil.ReadFile(filename); err != nil { + return appGenTxs, persistentPeers, err } var genStdTx auth.StdTx - err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx) - if err != nil { - return + if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil { + return appGenTxs, persistentPeers, err } appGenTxs = append(appGenTxs, genStdTx) - nodeAddr := genStdTx.GetMemo() - if len(nodeAddr) == 0 { - err = fmt.Errorf("couldn't find node's address in %s", fo.Name()) - return + // the memo flag is used to store + // the ip and node-id, for example this may be: + // "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656" + nodeAddrIP := genStdTx.GetMemo() + if len(nodeAddrIP) == 0 { + return appGenTxs, persistentPeers, fmt.Errorf( + "couldn't find node's address and IP in %s", fo.Name()) } + // genesis transactions must be single-message msgs := genStdTx.GetMsgs() if len(msgs) != 1 { - err = errors.New("each genesis transaction must provide a single genesis message") - return + + return appGenTxs, persistentPeers, errors.New( + "each genesis transaction must provide a single genesis message") } - // TODO: this could be decoupled from stake.MsgCreateValidator - // TODO: and we likely want to do it for real world Gaia + // validate the validator address and funds against the accounts in the state msg := msgs[0].(stake.MsgCreateValidator) - validators = append(validators, tmtypes.GenesisValidator{ - PubKey: msg.PubKey, - Power: freeFermionVal, - Name: msg.Description.Moniker, - }) + addr := string(sdk.AccAddress(msg.ValidatorAddr)) + acc, ok := addrMap[addr] + if !ok { + return appGenTxs, persistentPeers, fmt.Errorf( + "account %v not in genesis.json: %+v", addr, addrMap) + } + if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) { + err = fmt.Errorf("insufficient fund for the delegation: %s < %s", + acc.Coins.AmountOf(msg.Delegation.Denom), msg.Delegation.Amount) + } // exclude itself from persistent peers if msg.Description.Moniker != moniker { - addresses = append(addresses, nodeAddr) + addressesIPs = append(addressesIPs, nodeAddrIP) } } - sort.Strings(addresses) - persistentPeers = strings.Join(addresses, ",") + sort.Strings(addressesIPs) + persistentPeers = strings.Join(addressesIPs, ",") - return + return appGenTxs, persistentPeers, nil } func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 1acc9f3931..6c331856c3 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -1,11 +1,13 @@ package app import ( + "encoding/json" + "github.com/tendermint/tendermint/crypto/secp256k1" + tmtypes "github.com/tendermint/tendermint/types" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/stretchr/testify/require" @@ -27,7 +29,8 @@ var ( func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { // start with the default staking genesis state - stakeData := stake.DefaultGenesisState() + appState := NewDefaultGenesisState() + stakeData := appState.StakeData genAccs := make([]GenesisAccount, len(genTxs)) for i, genTx := range genTxs { @@ -35,17 +38,15 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { require.Equal(t, 1, len(msgs)) msg := msgs[0].(stake.MsgCreateValidator) - // get genesis flag account information - genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply + acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr)) + acc.Coins = sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)} + genAccs[i] = NewGenesisAccount(&acc) + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(150)) // increase the supply } // create the final app state - return GenesisState{ - Accounts: genAccs, - StakeData: stakeData, - GovData: gov.DefaultGenesisState(), - } + appState.Accounts = genAccs + return appState } func TestToAccount(t *testing.T) { @@ -68,6 +69,19 @@ func TestGaiaAppGenTx(t *testing.T) { func TestGaiaAppGenState(t *testing.T) { cdc := MakeCodec() _ = cdc + var genDoc tmtypes.GenesisDoc + + // test unmarshalling error + _, err := GaiaAppGenState(cdc, genDoc, []json.RawMessage{}) + require.Error(t, err) + + appState := makeGenesisState(t, []auth.StdTx{}) + genDoc.AppState, err = json.Marshal(appState) + require.NoError(t, err) + + // test validation error + _, err = GaiaAppGenState(cdc, genDoc, []json.RawMessage{}) + require.Error(t, err) // TODO test must provide at least genesis transaction // TODO test with both one and two genesis transactions: @@ -77,7 +91,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("steak", 50), desc, comm) + msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(bondDenom, + 50), desc, comm) return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "") } @@ -106,3 +121,10 @@ func TestGaiaGenesisValidation(t *testing.T) { err = GaiaValidateGenesisState(genesisState) require.NotNil(t, err) } + +func TestNewDefaultGenesisAccount(t *testing.T) { + addr := secp256k1.GenPrivKeySecp256k1([]byte("")).PubKey().Address() + acc := NewDefaultGenesisAccount(sdk.AccAddress(addr)) + require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("fooToken")) + require.Equal(t, sdk.NewInt(150), acc.Coins.AmountOf(bondDenom)) +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index b4791bf126..c80b597c75 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -5,9 +5,11 @@ package clitest import ( "encoding/json" "fmt" + "github.com/tendermint/tendermint/types" "io/ioutil" "os" "path" + "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -207,7 +209,7 @@ func TestGaiaCLIGasAuto(t *testing.T) { func TestGaiaCLICreateValidator(t *testing.T) { chainID, servAddr, port := initializeFixtures(t) - flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) + flags := fmt.Sprintf("--home=%s --chain-id=%v --node=%s", gaiacliHome, chainID, servAddr) // start gaiad server proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) @@ -544,7 +546,7 @@ func TestGaiaCLIConfig(t *testing.T) { servAddr, port, err := server.FreeTCPAddr() require.NoError(t, err) node := fmt.Sprintf("%s:%s", servAddr, port) - chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + chainID := executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y") config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) require.NoError(t, err) @@ -594,12 +596,27 @@ func initializeFixtures(t *testing.T) (chainID, servAddr, port string) { tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - - chainID = executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) - + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf( + "gaiacli keys show foo --output=json --home=%s", gaiacliHome)) + chainID = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) + genFile := filepath.Join(gaiadHome, "config", "genesis.json") + genDoc := readGenesisFile(t, genFile) + var appState app.GenesisState + err := codec.Cdc.UnmarshalJSON(genDoc.AppState, &appState) + require.NoError(t, err) + appState.Accounts = []app.GenesisAccount{app.NewDefaultGenesisAccount(fooAddr)} + appStateJSON, err := codec.Cdc.MarshalJSON(appState) + require.NoError(t, err) + genDoc.AppState = appStateJSON + genDoc.SaveAs(genFile) + executeWrite(t, fmt.Sprintf( + "gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome), + app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiad collect-gentxs --home=%s", gaiadHome), app.DefaultKeyPass) // get a free port, also setup some common flags - servAddr, port, err := server.FreeTCPAddr() + servAddr, port, err = server.FreeTCPAddr() require.NoError(t, err) return } @@ -618,6 +635,18 @@ func writeToNewTempFile(t *testing.T, s string) *os.File { return fp } +func readGenesisFile(t *testing.T, genFile string) types.GenesisDoc { + var genDoc types.GenesisDoc + fp, err := os.Open(genFile) + require.NoError(t, err) + fileContents, err := ioutil.ReadAll(fp) + require.NoError(t, err) + defer fp.Close() + err = codec.Cdc.UnmarshalJSON(fileContents, &genDoc) + require.NoError(t, err) + return genDoc +} + //___________________________________________________________________________________ // executors diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index ed777bf478..ae076571b5 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -39,7 +39,8 @@ func main() { } appInit := app.GaiaAppInit() rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit)) - rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) + rootCmd.AddCommand(gaiaInit.CollectGenTxsCmd(ctx, cdc)) + rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, server.AppInit{})) rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) server.AddCommands(ctx, cdc, rootCmd, appInit, diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go new file mode 100644 index 0000000000..34b65e560b --- /dev/null +++ b/cmd/gaia/init/collect.go @@ -0,0 +1,119 @@ +package init + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/go-amino" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/types" +) + +type initConfig struct { + ChainID string + GenTxsDir string + Name string + NodeID string + ValPubKey crypto.PubKey +} + +// nolint +func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "collect-gentxs", + Short: "Collect genesis txs and output a genesis.json file", + RunE: func(_ *cobra.Command, _ []string) error { + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + + name := viper.GetString(client.FlagName) + nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) + if err != nil { + return err + } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + + toPrint := printInfo{ + Moniker: config.Moniker, + ChainID: genDoc.ChainID, + NodeID: nodeID, + } + + initCfg := initConfig{ + ChainID: genDoc.ChainID, + GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), + Name: name, + NodeID: nodeID, + ValPubKey: valPubKey, + } + appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + if err != nil { + return err + } + + toPrint.AppMessage = appMessage + // print out some key information + return displayInfo(cdc, toPrint) + }, + } + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + return cmd +} + +func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig, + genDoc types.GenesisDoc) (appState json.RawMessage, err error) { + + genFile := config.GenesisFile() + // process genesis transactions, else create default genesis.json + var appGenTxs []auth.StdTx + var persistentPeers string + var genTxs []json.RawMessage + var jsonRawTx json.RawMessage + + appGenTxs, persistentPeers, err = app.CollectStdTxs( + cdc, config.Moniker, initCfg.GenTxsDir, genDoc) + if err != nil { + return + } + genTxs = make([]json.RawMessage, len(appGenTxs)) + config.P2P.PersistentPeers = persistentPeers + for i, stdTx := range appGenTxs { + jsonRawTx, err = cdc.MarshalJSON(stdTx) + if err != nil { + return + } + genTxs[i] = jsonRawTx + } + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs) + if err != nil { + return + } + + err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState) + return +} + +func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { + genContents, err := ioutil.ReadFile(genFile) + if err != nil { + return + } + err = cdc.UnmarshalJSON(genContents, &genDoc) + return +} diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 1b24a35768..7ed8203754 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -55,9 +55,20 @@ following delegation and commission default parameters: if err != nil { return err } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + // Read --pubkey, if empty take it from priv_validator.json + if valPubKeyString := viper.GetString(cli.FlagPubKey); valPubKeyString != "" { + valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString) + if err != nil { + return err + } + } // Run gaiad tx create-validator - prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey) + prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) createValidatorCmd := cli.GetCmdCreateValidator(cdc) w, err := ioutil.TempFile("", "gentx") @@ -82,28 +93,41 @@ following delegation and commission default parameters: }, } + cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") + cmd.Flags().AddFlagSet(cli.FsCommissionCreate) + cmd.Flags().AddFlagSet(cli.FsAmount) + cmd.Flags().AddFlagSet(cli.FsPk) cmd.MarkFlagRequired(client.FlagName) return cmd } -func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip string, valPubKey crypto.PubKey) { - viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home +func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID string, + valPubKey crypto.PubKey) { + viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home + viper.Set(client.FlagChainID, chainID) viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from viper.Set(cli.FlagNodeID, nodeID) // --node-id viper.Set(cli.FlagIP, ip) // --ip viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey - viper.Set(cli.FlagAmount, defaultAmount) // --amount - viper.Set(cli.FlagCommissionRate, defaultCommissionRate) - viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) - viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) - viper.Set(cli.FlagGenesisFormat, true) // --genesis-format - viper.Set(cli.FlagMoniker, config.Moniker) // --moniker + viper.Set(cli.FlagGenesisFormat, true) // --genesis-format + viper.Set(cli.FlagMoniker, config.Moniker) // --moniker if config.Moniker == "" { viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) } + if viper.GetString(cli.FlagAmount) == "" { + viper.Set(cli.FlagAmount, defaultAmount) + } + if viper.GetString(cli.FlagCommissionRate) == "" { + viper.Set(cli.FlagCommissionRate, defaultCommissionRate) + } + if viper.GetString(cli.FlagCommissionMaxRate) == "" { + viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) + } + if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" { + viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + } } func prepareFlagsForTxSign() { diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 467ea3fc2e..0290ae4fb2 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -2,24 +2,19 @@ package init import ( "encoding/json" - "errors" "fmt" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/x/auth" - authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/privval" "os" "path/filepath" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" @@ -27,26 +22,11 @@ import ( ) const ( - flagWithTxs = "with-txs" - flagOverwrite = "overwrite" - flagClientHome = "home-client" - flagOverwriteKey = "overwrite-key" - flagSkipGenesis = "skip-genesis" - flagMoniker = "moniker" + flagOverwrite = "overwrite" + flagClientHome = "home-client" + flagMoniker = "moniker" ) -type initConfig struct { - ChainID string - GenTxsDir string - Name string - NodeID string - ClientHome string - WithTxs bool - Overwrite bool - OverwriteKey bool - ValPubKey crypto.PubKey -} - type printInfo struct { Moniker string `json:"moniker"` ChainID string `json:"chain_id"` @@ -70,22 +50,16 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob cmd := &cobra.Command{ Use: "init", Short: "Initialize private validator, p2p, genesis, and application configuration files", - Long: `Initialize validators's and node's configuration files. - -Note that only node's configuration files will be written if the flag --skip-genesis is -enabled, and the genesis file will not be generated. -`, - Args: cobra.NoArgs, + Long: `Initialize validators's and node's configuration files.`, + Args: cobra.NoArgs, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) - - name := viper.GetString(client.FlagName) chainID := viper.GetString(client.FlagChainID) if chainID == "" { chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) } - nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) + nodeID, _, err := InitializeNodeValidatorFiles(config) if err != nil { return err } @@ -93,37 +67,26 @@ enabled, and the genesis file will not be generated. if viper.GetString(flagMoniker) != "" { config.Moniker = viper.GetString(flagMoniker) } - if config.Moniker == "" && name != "" { - config.Moniker = name - } - toPrint := printInfo{ - ChainID: chainID, - Moniker: config.Moniker, - NodeID: nodeID, - } - if viper.GetBool(flagSkipGenesis) { - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - return displayInfo(cdc, toPrint) - } - initCfg := initConfig{ - ChainID: chainID, - GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), - Name: name, - NodeID: nodeID, - ClientHome: viper.GetString(flagClientHome), - WithTxs: viper.GetBool(flagWithTxs), - Overwrite: viper.GetBool(flagOverwrite), - OverwriteKey: viper.GetBool(flagOverwriteKey), - ValPubKey: valPubKey, + var appState json.RawMessage + genFile := config.GenesisFile() + if appState, err = initializeEmptyGenesis(cdc, genFile, chainID, + viper.GetBool(flagOverwrite)); err != nil { + return err } - appMessage, err := initWithConfig(cdc, config, initCfg) - // print out some key information - if err != nil { + if err = WriteGenesisFile(genFile, chainID, nil, appState); err != nil { return err } - toPrint.AppMessage = appMessage + toPrint := printInfo{ + ChainID: chainID, + Moniker: config.Moniker, + NodeID: nodeID, + AppMessage: appState, + } + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return displayInfo(cdc, toPrint) }, } @@ -131,12 +94,7 @@ enabled, and the genesis file will not be generated. cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/") - cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") - cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag") - cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key") - cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json") + cmd.Flags().String(flagMoniker, "", "set the validator's moniker") return cmd } @@ -151,105 +109,6 @@ func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey return } -func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) ( - appMessage json.RawMessage, err error) { - genFile := config.GenesisFile() - if !initCfg.Overwrite && common.FileExists(genFile) { - err = fmt.Errorf("genesis.json file already exists: %v", genFile) - return - } - - // process genesis transactions, else create default genesis.json - var appGenTxs []auth.StdTx - var persistentPeers string - var genTxs []json.RawMessage - var appState json.RawMessage - var jsonRawTx json.RawMessage - chainID := initCfg.ChainID - - if initCfg.WithTxs { - _, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc) - if err != nil { - return - } - genTxs = make([]json.RawMessage, len(appGenTxs)) - config.P2P.PersistentPeers = persistentPeers - for i, stdTx := range appGenTxs { - jsonRawTx, err = cdc.MarshalJSON(stdTx) - if err != nil { - return - } - genTxs[i] = jsonRawTx - } - } else { - var ip, keyPass, secret string - var addr sdk.AccAddress - var signedTx auth.StdTx - - if initCfg.Name == "" { - err = errors.New("must specify validator's moniker (--name)") - return - } - - config.Moniker = initCfg.Name - ip, err = server.ExternalIP() - if err != nil { - return - } - memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip) - buf := client.BufferStdin() - prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.DefaultKeyPass) - keyPass, err = client.GetPassword(prompt, buf) - if err != nil && keyPass != "" { - // An error was returned that either failed to read the password from - // STDIN or the given password is not empty but failed to meet minimum - // length requirements. - return - } - if keyPass == "" { - keyPass = app.DefaultKeyPass - } - - addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey) - if err != nil { - return - } - appMessage, err = json.Marshal(map[string]string{"secret": secret}) - if err != nil { - return - } - - msg := stake.NewMsgCreateValidator( - sdk.ValAddress(addr), - initCfg.ValPubKey, - sdk.NewInt64Coin("steak", 100), - stake.NewDescription(config.Moniker, "", "", ""), - stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - ) - txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID) - signedTx, err = txBldr.SignStdTx( - initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo), false, - ) - if err != nil { - return - } - jsonRawTx, err = cdc.MarshalJSON(signedTx) - if err != nil { - return - } - genTxs = []json.RawMessage{jsonRawTx} - } - - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - appState, err = app.GaiaAppGenStateJSON(cdc, genTxs) - if err != nil { - return - } - err = WriteGenesisFile(genFile, chainID, nil, appState) - - return -} - // WriteGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. // nolint: unparam @@ -279,3 +138,13 @@ func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey { } return privValidator.GetPubKey() } + +func initializeEmptyGenesis(cdc *codec.Codec, genFile string, chainID string, + overwrite bool) (appState json.RawMessage, err error) { + if !overwrite && common.FileExists(genFile) { + err = fmt.Errorf("genesis.json file already exists: %v", genFile) + return + } + + return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState()) +} diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index 48a5d9247c..bd5274c261 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -42,7 +42,6 @@ func setupClientHome(t *testing.T) func() { clientDir, err := ioutil.TempDir("", "mock-sdk-cmd") require.Nil(t, err) viper.Set(flagClientHome, clientDir) - viper.Set(flagOverwriteKey, true) return func() { if err := os.RemoveAll(clientDir); err != nil { // TODO: Handle with #870 diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 3002b83a0e..5f729b0a4b 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -10,6 +10,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" + "github.com/tendermint/tendermint/types" "net" "os" "path/filepath" @@ -35,7 +36,9 @@ var ( const nodeDirPerm = 0755 // get cmd to initialize all files for tendermint testnet and application -func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { +func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, + appInit server.AppInit) *cobra.Command { + cmd := &cobra.Command{ Use: "testnet", Short: "Initialize files for a Gaiad testnet", @@ -50,7 +53,7 @@ Example: `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config - return testnetWithConfig(config, cdc, appInit) + return testnetWithConfig(config, cdc) }, } cmd.Flags().Int(nValidators, 4, @@ -69,7 +72,7 @@ Example: return cmd } -func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppInit) error { +func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { outDir := viper.GetString(outputDir) numValidators := viper.GetInt(nValidators) @@ -80,6 +83,8 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI valPubKeys := make([]crypto.PubKey, numValidators) // Generate private key, node ID, initial transaction + var accs []app.GenesisAccount + var genFiles []string for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDaemonHomeName := viper.GetString(nodeDaemonHome) @@ -115,8 +120,12 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI } memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + // write genesis + genFiles = append(genFiles, config.GenesisFile()) + buf := client.BufferStdin() - prompt := fmt.Sprintf("Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) + prompt := fmt.Sprintf( + "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) keyPass, err := client.GetPassword(prompt, buf) if err != nil && keyPass != "" { // An error was returned that either failed to read the password from @@ -138,12 +147,20 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI if err != nil { return err } - // Save private key seed words + // save private key seed words err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint) if err != nil { return err } + accs = append(accs, app.GenesisAccount{ + Address: addr, + Coins: sdk.Coins{ + sdk.NewInt64Coin(fmt.Sprintf("%sToken", nodeDirName), 1000), + sdk.NewInt64Coin("steak", 150), + }, + }) + msg := stake.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], @@ -173,8 +190,27 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI } } - for i := 0; i < numValidators; i++ { + // Generate empty genesis.json + appGenState := app.NewDefaultGenesisState() + appGenState.Accounts = accs + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) + if err != nil { + return err + } + genDoc := types.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + // Save all genesis.json files + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + + for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDaemonHomeName := viper.GetString(nodeDaemonHome) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) @@ -186,16 +222,17 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI nodeID, valPubKey := nodeIDs[i], valPubKeys[i] // Run `init` and generate genesis.json and config.toml initCfg := initConfig{ - ChainID: chainID, - GenTxsDir: gentxsDir, - Name: moniker, - WithTxs: true, - Overwrite: true, - OverwriteKey: false, - NodeID: nodeID, - ValPubKey: valPubKey, + ChainID: chainID, + GenTxsDir: gentxsDir, + Name: moniker, + NodeID: nodeID, + ValPubKey: valPubKey, } - if _, err := initWithConfig(cdc, config, initCfg); err != nil { + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + if _, err := genAppStateFromConfig(cdc, config, initCfg, genDoc); err != nil { return err } } diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index fc23db78fd..4f40a14509 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -41,7 +41,6 @@ func main() { appInit := server.DefaultAppInit rootCmd.AddCommand(InitCmd(ctx, cdc, appInit)) - rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) server.AddCommands(ctx, cdc, rootCmd, appInit, newApp, exportAppStateAndTMValidators) @@ -85,7 +84,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } - appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + appState, err := appInit.AppGenState( + cdc, tmtypes.GenesisDoc{}, []json.RawMessage{genTx}) if err != nil { return err } @@ -108,13 +108,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + []tmtypes.GenesisValidator{validator}, appStateJSON) }, } cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagChainID, "", + "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.MarkFlagRequired(client.FlagName) return cmd @@ -124,7 +126,8 @@ func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Applicatio return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) ( + json.RawMessage, []tmtypes.GenesisValidator, error) { bapp := app.NewBasecoinApp(logger, db) return bapp.ExportAppStateAndValidators() } diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index b0256b1749..2f48a64f34 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -36,8 +36,9 @@ var CoolAppInit = server.AppInit{ } // coolGenAppParams sets up the app_state and appends the cool app state -func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { - appState, err = server.SimpleAppGenState(cdc, appGenTxs) +func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { + appState, err = server.SimpleAppGenState(cdc, tmtypes.GenesisDoc{}, appGenTxs) if err != nil { return } @@ -90,7 +91,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } - appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + appState, err := appInit.AppGenState(cdc, tmtypes.GenesisDoc{}, + []json.RawMessage{genTx}) if err != nil { return err } @@ -113,13 +115,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + []tmtypes.GenesisValidator{validator}, appStateJSON) }, } cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagChainID, "", + "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.MarkFlagRequired(client.FlagName) return cmd @@ -129,7 +133,8 @@ func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { return app.NewDemocoinApp(logger, db) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) ( + json.RawMessage, []tmtypes.GenesisValidator, error) { dapp := app.NewDemocoinApp(logger, db) return dapp.ExportAppStateAndValidators() } diff --git a/server/init.go b/server/init.go index 6a5d821c17..75e13f4526 100644 --- a/server/init.go +++ b/server/init.go @@ -19,7 +19,8 @@ import ( type AppInit struct { // AppGenState creates the core parameters initialization. It takes in a // pubkey meant to represent the pubkey of the validator of this machine. - AppGenState func(cdc *codec.Codec, appGenTx []json.RawMessage) (appState json.RawMessage, err error) + AppGenState func(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) } // SimpleGenTx is a simple genesis tx @@ -35,7 +36,8 @@ var DefaultAppInit = AppInit{ } // Generate a genesis transaction -func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { +func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) ( + appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { var addr sdk.AccAddress var secret string addr, secret, err = GenerateCoinKey() @@ -63,7 +65,8 @@ func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json } // create the genesis app state -func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func SimpleAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { if len(appGenTxs) != 1 { err = errors.New("must provide a single genesis transaction") diff --git a/server/mock/app.go b/server/mock/app.go index 18afa164ec..dd8f3f5aba 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -3,6 +3,7 @@ package mock import ( "encoding/json" "fmt" + "github.com/tendermint/tendermint/types" "path/filepath" abci "github.com/tendermint/tendermint/abci/types" @@ -102,7 +103,8 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci // AppGenState can be passed into InitCmd, returns a static string of a few // key-values that can be parsed by InitChainer -func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenState(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (appState json. + RawMessage, err error) { appState = json.RawMessage(`{ "values": [ { @@ -119,7 +121,8 @@ func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, } // AppGenStateEmpty returns an empty transaction state for mocking. -func AppGenStateEmpty(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenStateEmpty(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) ( + appState json.RawMessage, err error) { appState = json.RawMessage(``) return } diff --git a/server/mock/app_test.go b/server/mock/app_test.go index 05ec865219..a5f2a078b2 100644 --- a/server/mock/app_test.go +++ b/server/mock/app_test.go @@ -1,6 +1,7 @@ package mock import ( + "github.com/tendermint/tendermint/types" "testing" "github.com/stretchr/testify/require" @@ -20,7 +21,7 @@ func TestInitApp(t *testing.T) { require.NoError(t, err) // initialize it future-way - appState, err := AppGenState(nil, nil) + appState, err := AppGenState(nil, types.GenesisDoc{}, nil) require.NoError(t, err) //TODO test validators in the init chain? diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index e4948f9456..d571bef9eb 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -35,11 +35,11 @@ const ( // common flagsets to add to various functions var ( - fsPk = flag.NewFlagSet("", flag.ContinueOnError) - fsAmount = flag.NewFlagSet("", flag.ContinueOnError) + FsPk = flag.NewFlagSet("", flag.ContinueOnError) + FsAmount = flag.NewFlagSet("", flag.ContinueOnError) fsShares = flag.NewFlagSet("", flag.ContinueOnError) fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError) - fsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) + FsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError) fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError) fsValidator = flag.NewFlagSet("", flag.ContinueOnError) @@ -48,8 +48,8 @@ var ( ) func init() { - fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220") - fsAmount.String(FlagAmount, "", "Amount of coins to bond") + FsPk.String(FlagPubKey, "", "Bech32-encoded PubKey of the validator. ") + FsAmount.String(FlagAmount, "", "Amount of coins to bond") fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") fsShares.String(FlagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") fsDescriptionCreate.String(FlagMoniker, "", "validator name") @@ -57,9 +57,9 @@ func init() { fsDescriptionCreate.String(FlagWebsite, "", "optional website") fsDescriptionCreate.String(FlagDetails, "", "optional details") fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage") - fsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") - fsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") - fsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") + FsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "validator name") fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "optional identity signature (ex. UPort or Keybase)") fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "optional website") diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index c43e1276e3..3b8d8e2009 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -103,10 +103,10 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsPk) - cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().AddFlagSet(FsPk) + cmd.Flags().AddFlagSet(FsAmount) cmd.Flags().AddFlagSet(fsDescriptionCreate) - cmd.Flags().AddFlagSet(fsCommissionCreate) + cmd.Flags().AddFlagSet(FsCommissionCreate) cmd.Flags().AddFlagSet(fsDelegator) cmd.Flags().Bool(FlagGenesisFormat, false, "Export the transaction in gen-tx format; it implies --generate-only") cmd.Flags().String(FlagIP, "", fmt.Sprintf("Node's public IP. It takes effect only when used in combination with --%s", FlagGenesisFormat)) @@ -204,7 +204,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().AddFlagSet(FsAmount) cmd.Flags().AddFlagSet(fsValidator) return cmd From 256ec0f07b0559ab594da5cf9cae42203ccae7bc Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 5 Nov 2018 05:44:43 +0100 Subject: [PATCH 50/61] Simulation improvements (logging fix, random genesis parameters) (#2617) * Print out initial update on every block * Randomize simulation parameters * Randomize initial liveness weightings * Randomize genesis parameters * fixed power store invariant * IterateValidatorsBonded -> IterateBondedValidatorsByPower * WriteValidators uses IterateLastValidators rather than IterateBondedValidatorsByPower * fixed democoin interface Closes #2556 Closes #2396 Via #2671: closes #2669 closes #2670 closes #2620 Offshoot issues: #2618 #2619 #2620 #2661 --- PENDING.md | 5 ++ baseapp/baseapp.go | 1 + cmd/gaia/app/sim_test.go | 78 +++++++++++++++++---- examples/democoin/mock/validator.go | 9 ++- scripts/multisim.sh | 3 +- types/stake.go | 6 +- x/gov/tally.go | 2 +- x/mock/simulation/constants.go | 37 ---------- x/mock/simulation/params.go | 66 +++++++++++++++++ x/mock/simulation/random_simulate_blocks.go | 42 +++++------ x/stake/genesis.go | 2 +- x/stake/keeper/sdk_types.go | 26 ++++++- x/stake/keeper/validator.go | 14 ++++ x/stake/simulation/invariants.go | 32 ++++++--- 14 files changed, 236 insertions(+), 87 deletions(-) delete mode 100644 x/mock/simulation/constants.go create mode 100644 x/mock/simulation/params.go diff --git a/PENDING.md b/PENDING.md index 9acbf325b7..ad20be35f4 100644 --- a/PENDING.md +++ b/PENDING.md @@ -43,6 +43,10 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - #2556 [x/mock/simulation] Fix debugging output + - #2396 [x/mock/simulation] Change parameters to get more slashes + - #2617 [x/mock/simulation] Randomize all genesis parameters + - #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - \#1924 [x/mock/simulation] Use a transition matrix for block size - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - #2610 [x/stake] Block redelegation to and from the same validator @@ -58,6 +62,7 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia + - #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * SDK - #2625 [x/gov] fix AppendTag function usage error diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a2b2f41667..827536d215 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -394,6 +394,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger). WithMinimumFees(app.minimumFees) + // Passes the rest of the path as an argument to the querier. // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path resBytes, err := querier(ctx, path[2:], req) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 9f94a82f7c..0216616a88 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "testing" + "time" "github.com/stretchr/testify/require" @@ -50,42 +51,93 @@ func init() { func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { var genesisAccounts []GenesisAccount - amt := int64(10000) + amount := int64(r.Intn(1e6)) + numInitiallyBonded := int64(r.Intn(250)) + numAccs := int64(len(accs)) + if numInitiallyBonded > numAccs { + numInitiallyBonded = numAccs + } + fmt.Printf("Selected randomly generated parameters for simulated genesis: {amount of steak per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded) // Randomly generate some genesis accounts for _, acc := range accs { - coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amt)}} + coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amount)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ Address: acc.Address, Coins: coins, }) } - // Default genesis state - govGenesis := gov.DefaultGenesisState() - stakeGenesis := stake.DefaultGenesisState() - slashingGenesis := slashing.DefaultGenesisState() + // Random genesis states + govGenesis := gov.GenesisState{ + StartingProposalID: int64(r.Intn(100)), + DepositProcedure: gov.DepositProcedure{ + MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))}, + MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + }, + VotingProcedure: gov.VotingProcedure{ + VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + }, + TallyingProcedure: gov.TallyingProcedure{ + Threshold: sdk.NewDecWithPrec(5, 1), + Veto: sdk.NewDecWithPrec(334, 3), + GovernancePenalty: sdk.NewDecWithPrec(1, 2), + }, + } + fmt.Printf("Selected randomly generated governance parameters: %+v\n", govGenesis) + stakeGenesis := stake.GenesisState{ + Pool: stake.InitialPool(), + Params: stake.Params{ + UnbondingTime: time.Duration(r.Intn(60*60*24*3*2)) * time.Second, + MaxValidators: uint16(r.Intn(250)), + BondDenom: "steak", + }, + } + fmt.Printf("Selected randomly generated staking parameters: %+v\n", stakeGenesis) + slashingGenesis := slashing.GenesisState{ + Params: slashing.Params{ + MaxEvidenceAge: stakeGenesis.Params.UnbondingTime, + DoubleSignUnbondDuration: time.Duration(r.Intn(60*60*24)) * time.Second, + SignedBlocksWindow: int64(r.Intn(1000)), + DowntimeUnbondDuration: time.Duration(r.Intn(86400)) * time.Second, + MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1), + SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))), + SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))), + }, + } + fmt.Printf("Selected randomly generated slashing parameters: %+v\n", slashingGenesis) + mintGenesis := mint.GenesisState{ + Minter: mint.Minter{ + InflationLastTime: time.Unix(0, 0), + Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), + }, + Params: mint.Params{ + MintDenom: "steak", + InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), + InflationMax: sdk.NewDecWithPrec(20, 2), + InflationMin: sdk.NewDecWithPrec(7, 2), + GoalBonded: sdk.NewDecWithPrec(67, 2), + }, + } + fmt.Printf("Selected randomly generated minting parameters: %v\n", mintGenesis) var validators []stake.Validator var delegations []stake.Delegation - // XXX Try different numbers of initially bonded validators - numInitiallyBonded := int64(50) valAddrs := make([]sdk.ValAddress, numInitiallyBonded) for i := 0; i < int(numInitiallyBonded); i++ { valAddr := sdk.ValAddress(accs[i].Address) valAddrs[i] = valAddr validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{}) - validator.Tokens = sdk.NewDec(amt) - validator.DelegatorShares = sdk.NewDec(amt) - delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0} + validator.Tokens = sdk.NewDec(amount) + validator.DelegatorShares = sdk.NewDec(amount) + delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } - stakeGenesis.Pool.LooseTokens = sdk.NewDec(amt*250 + (numInitiallyBonded * amt)) + stakeGenesis.Pool.LooseTokens = sdk.NewDec((amount * numAccs) + (numInitiallyBonded * amount)) stakeGenesis.Validators = validators stakeGenesis.Bonds = delegations - mintGenesis := mint.DefaultGenesisState() genesis := GenesisState{ Accounts: genesisAccounts, diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index fc00b79be0..1d10c48b24 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -82,8 +82,13 @@ func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64, } } -// IterateValidatorsBonded implements sdk.ValidatorSet -func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { +// IterateBondedValidatorsByPower implements sdk.ValidatorSet +func (vs *ValidatorSet) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { + vs.IterateValidators(ctx, fn) +} + +// IterateLastValidators implements sdk.ValidatorSet +func (vs *ValidatorSet) IterateLastValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { vs.IterateValidators(ctx, fn) } diff --git a/scripts/multisim.sh b/scripts/multisim.sh index 8ffa338b84..ff5784e4bf 100755 --- a/scripts/multisim.sh +++ b/scripts/multisim.sh @@ -1,6 +1,7 @@ #!/bin/bash -seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391) +seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \ +11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827) blocks=$1 echo "Running multi-seed simulation with seeds ${seeds[@]}" diff --git a/types/stake.go b/types/stake.go index f5d3a4aae0..7b10b17f87 100644 --- a/types/stake.go +++ b/types/stake.go @@ -64,7 +64,11 @@ type ValidatorSet interface { func(index int64, validator Validator) (stop bool)) // iterate through bonded validators by operator address, execute func for each validator - IterateValidatorsBonded(Context, + IterateBondedValidatorsByPower(Context, + func(index int64, validator Validator) (stop bool)) + + // iterate through the consensus validator set of the last block by operator address, execute func for each validator + IterateLastValidators(Context, func(index int64, validator Validator) (stop bool)) Validator(Context, ValAddress) Validator // get a particular validator by operator address diff --git a/x/gov/tally.go b/x/gov/tally.go index 2bfaa7dd55..e839bbe039 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -23,7 +23,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall totalVotingPower := sdk.ZeroDec() currValidators := make(map[string]validatorGovInfo) - keeper.vs.IterateValidatorsBonded(ctx, func(index int64, validator sdk.Validator) (stop bool) { + keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) { currValidators[validator.GetOperator().String()] = validatorGovInfo{ Address: validator.GetOperator(), Power: validator.GetPower(), diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go deleted file mode 100644 index 2c4543b72d..0000000000 --- a/x/mock/simulation/constants.go +++ /dev/null @@ -1,37 +0,0 @@ -package simulation - -const ( - // Fraction of double-signing evidence from a past height - pastEvidenceFraction float64 = 0.5 - - // Minimum time per block - minTimePerBlock int64 = 1000 / 2 - - // Maximum time per block - maxTimePerBlock int64 = 1000 - - // Number of keys - numKeys int = 250 - - // Chance that double-signing evidence is found on a given block - evidenceFraction float64 = 0.5 - - // TODO Remove in favor of binary search for invariant violation - onOperation bool = false -) - -var ( - // Currently there are 3 different liveness types, fully online, spotty connection, offline. - initialLivenessWeightings = []int{40, 5, 5} - livenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{ - {90, 20, 1}, - {10, 50, 5}, - {0, 10, 1000}, - }) - // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 - blockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ - {85, 5, 0}, - {15, 92, 1}, - {0, 3, 99}, - }) -) diff --git a/x/mock/simulation/params.go b/x/mock/simulation/params.go new file mode 100644 index 0000000000..404a85e544 --- /dev/null +++ b/x/mock/simulation/params.go @@ -0,0 +1,66 @@ +package simulation + +import ( + "math/rand" +) + +const ( + // Minimum time per block + minTimePerBlock int64 = 10000 / 2 + + // Maximum time per block + maxTimePerBlock int64 = 10000 + + // TODO Remove in favor of binary search for invariant violation + onOperation bool = false +) + +var ( + // Currently there are 3 different liveness types, fully online, spotty connection, offline. + 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 + defaultBlockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ + {85, 5, 0}, + {15, 92, 1}, + {0, 3, 99}, + }) +) + +// Simulation parameters +type Params struct { + PastEvidenceFraction float64 + NumKeys int + EvidenceFraction float64 + InitialLivenessWeightings []int + LivenessTransitionMatrix TransitionMatrix + BlockSizeTransitionMatrix TransitionMatrix +} + +// Return default simulation parameters +func DefaultParams() Params { + return Params{ + PastEvidenceFraction: 0.5, + NumKeys: 250, + EvidenceFraction: 0.5, + InitialLivenessWeightings: []int{40, 5, 5}, + LivenessTransitionMatrix: defaultLivenessTransitionMatrix, + BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, + } +} + +// Return random simulation parameters +func RandomParams(r *rand.Rand) Params { + return Params{ + PastEvidenceFraction: r.Float64(), + NumKeys: r.Intn(250), + EvidenceFraction: r.Float64(), + InitialLivenessWeightings: []int{r.Intn(80), r.Intn(10), r.Intn(10)}, + LivenessTransitionMatrix: defaultLivenessTransitionMatrix, + BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, + } +} diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index a4da39523e..5784e7a223 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -31,13 +31,13 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } -func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, +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, initialLivenessWeightings)} + validators[str] = mockValidator{validator, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} } for i := 0; i < len(setups); i++ { @@ -65,11 +65,13 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, 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) + 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, numKeys) + accs := RandomAccounts(r, params.NumKeys) // Setup event stats events := make(map[string]uint) @@ -77,7 +79,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, events[what]++ } - validators := initChain(r, accs, setups, app, appStateFn) + 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 @@ -90,7 +92,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) go func() { receivedSignal := <-c - fmt.Printf("Exiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) + 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 }() @@ -98,7 +100,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, var pastTimes []time.Time var pastVoteInfos [][]abci.VoteInfo - request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) + 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{} @@ -108,7 +110,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, blockLogBuilders = make([]*strings.Builder, numBlocks) } displayLogs := logPrinter(testingMode, blockLogBuilders) - blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) + blockSimulator := createBlockSimulator(testingMode, tb, t, params, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) if !testingMode { b.ResetTimer() } else { @@ -181,11 +183,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } // Generate a random RequestBeginBlock with the current validator set for the next block - request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) + 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, validators, res.ValidatorUpdates, event) + nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, event) } if stopEarly { DisplayEvents(events) @@ -203,7 +205,7 @@ type blockSimFn func( // 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, +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 { @@ -231,7 +233,8 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { - lastBlocksizeState, blocksize = getBlockSize(r, lastBlocksizeState, avgBlockSize) + 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) @@ -272,11 +275,10 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B // "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, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { - // TODO: Make blockSizeTransitionMatrix non-global +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 = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState) + state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState) if state == 0 { blocksize = r.Intn(avgBlockSize * 4) } else if state == 1 { @@ -379,7 +381,7 @@ func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.He // 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, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64, +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} @@ -388,7 +390,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, i := 0 for _, key := range getKeys(validators) { mVal := validators[key] - mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState) + mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) signed := true if mVal.livenessState == 1 { @@ -422,11 +424,11 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, evidence := make([]abci.Evidence, 0) // Anything but the first block if len(pastTimes) > 0 { - for r.Float64() < evidenceFraction { + for r.Float64() < params.EvidenceFraction { height := header.Height time := header.Time vals := voteInfos - if r.Float64() < pastEvidenceFraction { + if r.Float64() < params.PastEvidenceFraction { height = int64(r.Intn(int(header.Height) - 1)) time = pastTimes[height] vals = pastVoteInfos[height] @@ -457,7 +459,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, // updateValidators mimicks Tendermint's update logic // nolint: unparam -func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { +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) @@ -476,7 +478,7 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida event("endblock/validatorupdates/updated") } else { // Set this new validator - current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)} + current[str] = mockValidator{update, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} event("endblock/validatorupdates/added") } } diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 28c5973c74..63b038613d 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -72,7 +72,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { // WriteValidators returns a slice of bonded genesis validators. func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { - keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) { + keeper.IterateLastValidators(ctx, func(_ int64, validator sdk.Validator) (stop bool) { vals = append(vals, tmtypes.GenesisValidator{ PubKey: validator.GetConsPubKey(), Power: validator.GetPower().RoundInt64(), diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 4e859a42a8..1dea473f89 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -28,9 +28,31 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato } // iterate through the active validator set and perform the provided function -func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { +func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + maxValidators := k.MaxValidators(ctx) + + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid() && i < int64(maxValidators); iterator.Next() { + address := iterator.Value() + validator := k.mustGetValidator(ctx, address) + + if validator.Status == sdk.Bonded { + stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { + break + } + i++ + } + } +} + +// iterate through the active validator set and perform the provided function +func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { + iterator := k.LastValidatorsIterator(ctx) i := int64(0) for ; iterator.Valid(); iterator.Next() { address := AddressFromLastValidatorPowerKey(iterator.Key()) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 5770ada2ff..edf781bb57 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -261,6 +261,13 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator return validators[:i] // trim } +// returns an iterator for the consensus validators in the last block +func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + return iterator +} + // get the current group of bonded validators sorted by power-rank func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) @@ -283,6 +290,13 @@ func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { return validators[:i] // trim } +// returns an iterator for the current validator power store +func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + return iterator +} + // gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators // that expire at a certain time. func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) { diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 8e6f184321..819bc8b379 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -1,6 +1,7 @@ package simulation import ( + "bytes" "fmt" "github.com/cosmos/cosmos-sdk/baseapp" @@ -10,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/keeper" abci "github.com/tendermint/tendermint/abci/types" ) @@ -24,10 +26,12 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, if err != nil { return err } + err = PositivePowerInvariant(k)(app) if err != nil { return err } + err = ValidatorSetInvariant(k)(app) return err } @@ -100,19 +104,29 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, } } -// PositivePowerInvariant checks that all stored validators have > 0 power +// PositivePowerInvariant checks that all stored validators have > 0 power. func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) - var err error - k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { - if !validator.GetPower().GT(sdk.ZeroDec()) { - err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetConsPubKey()) - return true + + iterator := k.ValidatorsPowerStoreIterator(ctx) + pool := k.GetPool(ctx) + + for ; iterator.Valid(); iterator.Next() { + validator, found := k.GetValidator(ctx, iterator.Value()) + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value())) } - return false - }) - return err + + powerKey := keeper.GetValidatorsByPowerIndexKey(validator, pool) + + if !bytes.Equal(iterator.Key(), powerKey) { + return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+ + "\n\tkey should be: %v\n\tkey in store: %v", validator.GetPower(), powerKey, iterator.Key()) + } + } + iterator.Close() + return nil } } From 336415baea2950ccca4a9a20005de949943f5c6f Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 22:11:03 -0800 Subject: [PATCH 51/61] Fix simulation bugs; Incorprates #2676 from Sunny (#2677) * Fix simulation bugs; Incorprates #2676 from Sunny * Address review feedback; Update PENDING --- PENDING.md | 20 ++++++++--------- cmd/gaia/app/app.go | 14 +++++++----- x/distribution/keeper/hooks.go | 7 ++++-- x/distribution/keeper/test_common.go | 2 +- x/mock/simulation/random_simulate_blocks.go | 6 ++--- x/slashing/keeper.go | 10 ++++++++- x/slashing/test_common.go | 4 ++-- x/stake/handler.go | 25 +++++++++++++++------ x/stake/handler_test.go | 9 ++++---- x/stake/keeper/delegation.go | 7 ++++++ x/stake/keeper/keeper.go | 2 +- x/stake/keeper/slash.go | 7 +----- x/stake/keeper/slash_test.go | 24 ++++++++++---------- x/stake/keeper/val_state_change.go | 9 ++------ x/stake/keeper/validator.go | 7 ++++-- x/stake/keeper/validator_test.go | 10 +++++---- x/stake/types/errors.go | 5 +++++ x/stake/types/validator.go | 3 +++ 18 files changed, 103 insertions(+), 68 deletions(-) diff --git a/PENDING.md b/PENDING.md index ad20be35f4..11c13ea75f 100644 --- a/PENDING.md +++ b/PENDING.md @@ -42,15 +42,15 @@ IMPROVEMENTS * Gaia * SDK - - #2573 [x/distribution] add accum invariance - - #2556 [x/mock/simulation] Fix debugging output - - #2396 [x/mock/simulation] Change parameters to get more slashes - - #2617 [x/mock/simulation] Randomize all genesis parameters - - #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. + - \#2573 [x/distribution] add accum invariance + - \#2556 [x/mock/simulation] Fix debugging output + - \#2396 [x/mock/simulation] Change parameters to get more slashes + - \#2617 [x/mock/simulation] Randomize all genesis parameters + - \#2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - \#1924 [x/mock/simulation] Use a transition matrix for block size - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - - #2610 [x/stake] Block redelegation to and from the same validator - - #2652 [x/auth] Add benchmark for get and set account + - \#2610 [x/stake] Block redelegation to and from the same validator + - \#2652 [x/auth] Add benchmark for get and set account * Tendermint @@ -62,10 +62,10 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * SDK - - #2625 [x/gov] fix AppendTag function usage error - + - \#2625 [x/gov] fix AppendTag function usage error + - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator * Tendermint diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 5f1f61f1e4..1927d235d1 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -109,7 +109,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.cdc, app.keyParams, app.tkeyParams, ) - app.stakeKeeper = stake.NewKeeper( + stakeKeeper := stake.NewKeeper( app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), @@ -117,30 +117,32 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio ) app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.paramsKeeper.Subspace(mint.DefaultParamspace), - app.stakeKeeper, app.feeCollectionKeeper, + &stakeKeeper, app.feeCollectionKeeper, ) app.distrKeeper = distr.NewKeeper( app.cdc, app.keyDistr, app.paramsKeeper.Subspace(distr.DefaultParamspace), - app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper, + app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper, app.RegisterCodespace(stake.DefaultCodespace), ) app.slashingKeeper = slashing.NewKeeper( app.cdc, app.keySlashing, - app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), + &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), app.RegisterCodespace(slashing.DefaultCodespace), ) app.govKeeper = gov.NewKeeper( app.cdc, app.keyGov, - app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, app.stakeKeeper, + app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace), ) // register the staking hooks - app.stakeKeeper = app.stakeKeeper.WithHooks( + // NOTE: stakeKeeper above are passed by reference, + // so that it can be modified like below: + app.stakeKeeper = *stakeKeeper.SetHooks( NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) // register message routes diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index efd2c06104..cdebaf93c4 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -1,6 +1,8 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -38,11 +40,12 @@ func (k Keeper) onValidatorBonded(ctx sdk.Context, valAddr sdk.ValAddress) { k.onValidatorModified(ctx, valAddr) } -// XXX Consider removing this after debugging. +// Sanity check, very useful! func (k Keeper) onValidatorPowerDidChange(ctx sdk.Context, valAddr sdk.ValAddress) { vi := k.GetValidatorDistInfo(ctx, valAddr) if vi.FeePoolWithdrawalHeight != ctx.BlockHeight() { - panic("expected validator dist info FeePoolWithdrawalHeight to be updated, but was not.") + panic(fmt.Sprintf("expected validator (%v) dist info FeePoolWithdrawalHeight to be updated to %v, but was %v.", + valAddr.String(), ctx.BlockHeight(), vi.FeePoolWithdrawalHeight)) } } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 0a02fb245f..660abbd0e0 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -130,7 +130,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), ck, sk, fck, types.DefaultCodespace) // set the distribution hooks on staking - sk = sk.WithHooks(keeper.Hooks()) + sk.SetHooks(keeper.Hooks()) // set genesis items required for distribution keeper.SetFeePool(ctx, types.InitialFeePool()) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 5784e7a223..a568997e62 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -14,7 +14,7 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - common "github.com/tendermint/tendermint/libs/common" + cmn "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" @@ -65,7 +65,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, 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) + 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()) @@ -365,7 +365,7 @@ func getKeys(validators map[string]mockValidator) []string { } // randomProposer picks a random proposer from the current validator set -func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.HexBytes { +func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes { keys := getKeys(validators) if len(keys) == 0 { return nil diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 718a024350..4a52ddcb61 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -49,6 +49,15 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr)) } + // Get validator. + validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr) + if validator == nil || validator.GetStatus() == sdk.Unbonded { + // Defensive. + // Simulation doesn't take unbonding periods into account, and + // Tendermint might break this assumption at some point. + return + } + // Double sign too old maxEvidenceAge := k.MaxEvidenceAge(ctx) if age > maxEvidenceAge { @@ -80,7 +89,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, revisedFraction) // Jail validator if not already jailed - validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr) if !validator.GetJailed() { k.validatorSet.Jail(ctx, consAddr) } diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 55a6fda18f..2f1113aa13 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -87,8 +87,8 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s } require.Nil(t, err) paramstore := paramsKeeper.Subspace(DefaultParamspace) - keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace) - sk = sk.WithHooks(keeper.Hooks()) + keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, DefaultCodespace) + sk.SetHooks(keeper.Hooks()) require.NotPanics(t, func() { InitGenesis(ctx, keeper, GenesisState{defaults}, genesis) diff --git a/x/stake/handler.go b/x/stake/handler.go index 328a901330..83aec00eac 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -31,11 +31,27 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } // Called every block, update validator set -func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) { +func EndBlocker(ctx sdk.Context, k keeper.Keeper) (validatorUpdates []abci.ValidatorUpdate) { endBlockerTags := sdk.EmptyTags() + // Reset the intra-transaction counter. + k.SetIntraTxCounter(ctx, 0) + + // Calculate validator set changes. + // + // NOTE: ApplyAndReturnValidatorSetUpdates has to come before + // UnbondAllMatureValidatorQueue. + // This fixes a bug when the unbonding period is instant (is the case in + // some of the tests). The test expected the validator to be completely + // unbonded after the Endblocker (go from Bonded -> Unbonding during + // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during + // UnbondAllMatureValidatorQueue). + validatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) + + // Unbond all mature validators from the unbonding queue. k.UnbondAllMatureValidatorQueue(ctx) + // Remove all mature unbonding delegations from the ubd queue. matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr) @@ -49,6 +65,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid )) } + // Remove all mature redelegations from the red queue. matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr) @@ -62,12 +79,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid tags.DstValidator, []byte(dvvTriplet.ValidatorDstAddr.String()), )) } - - // reset the intra-transaction counter - k.SetIntraTxCounter(ctx, 0) - - // calculate validator set changes - ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) return } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index e5c78bcf6f..fcc268f558 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -494,11 +494,12 @@ func TestMultipleMsgCreateValidator(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time + // Jump to finishTime for unbonding period and remove from unbonding queue types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) - //Check that the account is unbonded + // Check that the validator is deleted from state validators := keeper.GetValidators(ctx, 100) require.Equal(t, len(validatorAddrs)-(i+1), len(validators), "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) @@ -1013,7 +1014,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { EndBlocker(ctx, keeper) // validator power should have been reduced to zero - // ergo validator should have been removed from the store - _, found = keeper.GetValidator(ctx, valA) - require.False(t, found) + // validator should be in unbonding state + validator, _ = keeper.GetValidator(ctx, valA) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 77f1323f2b..fbb62dcbff 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -349,6 +349,13 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Coin, validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) { + // In some situations, the exchange rate becomes invalid, e.g. if + // validator loses all tokens due to slashing. In this case, + // make all future delegations invalid. + if validator.DelegatorShareExRate().IsZero() { + return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace()) + } + // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr) if !found { diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index b61ead54c9..a74f86084f 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -36,7 +36,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramst } // Set the validator hooks -func (k Keeper) WithHooks(sh sdk.StakingHooks) Keeper { +func (k *Keeper) SetHooks(sh sdk.StakingHooks) *Keeper { if k.hooks != nil { panic("cannot set validator hooks twice") } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 5879428f2b..e7bd72764b 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -108,12 +108,6 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) k.SetPool(ctx, pool) - // remove validator if it has no more tokens - if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded { - // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // Log that a slash occurred! logger.Info(fmt.Sprintf( "validator %s slashed by slash factor of %s; burned %v tokens", @@ -236,6 +230,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re if sharesToUnbond.GT(delegation.Shares) { sharesToUnbond = delegation.Shares } + tokensToBurn, err := k.unbond(ctx, redelegation.DelegatorAddr, redelegation.ValidatorDstAddr, sharesToUnbond) if err != nil { panic(fmt.Errorf("error unbonding delegator: %v", err)) diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index aab97b811c..9c23576c34 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -348,9 +348,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator // power decreased by 1 again, validator is out of stake - // ergo validator should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with a redelegation @@ -450,16 +450,16 @@ func TestSlashWithRedelegation(t *testing.T) { // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator - // validator decreased to zero power, should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator decreased to zero power, should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) // slash the validator again, by 100% // no stake remains to be slashed ctx = ctx.WithBlockHeight(12) - // validator no longer in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) // read updating redelegation @@ -472,9 +472,9 @@ func TestSlashWithRedelegation(t *testing.T) { // no more bonded tokens burned require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - // power still zero, still not in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // power still zero, still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with both an unbonding delegation and a redelegation diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index c259fe0254..307270c16f 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -78,8 +78,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) - // XXX Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight. - // XXX This hook probably shouldn't exist. Maybe rethink the hook system. + // Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight. + // This hook is extremely useful, otherwise lazy accum bugs will be difficult to solve. if k.hooks != nil { k.hooks.OnValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr) } @@ -108,11 +108,6 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // bonded to unbonding k.bondedToUnbonding(ctx, validator) - // remove validator if it has no more tokens - if validator.Tokens.IsZero() { - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // delete from the bonded validator index k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes)) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index edf781bb57..4646480ac6 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -155,6 +155,7 @@ func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types. // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator { + pool := k.GetPool(ctx) k.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool = validator.RemoveTokens(pool, tokensToRemove) @@ -189,6 +190,9 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { if !found { return } + if validator.Status != sdk.Unbonded { + panic("Cannot call RemoveValidator on bonded or unbonding validators") + } // delete the old validator record store := ctx.KVStore(k.storeKey) @@ -357,10 +361,9 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { if !found || val.GetStatus() != sdk.Unbonding { continue } + k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() { k.RemoveValidator(ctx, val.OperatorAddr) - } else { - k.unbondingToUnbonded(ctx, val) } } store.Delete(validatorTimesliceIterator.Key()) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 7acf1cc02d..fe806335f0 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -186,9 +186,9 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - // validator should have been deleted - _, found := keeper.GetValidator(ctx, addrVals[0]) - require.False(t, found) + // validator should be unbonding + validator, _ = keeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator @@ -276,7 +276,9 @@ func TestValidatorBasics(t *testing.T) { assert.True(ValEq(t, validators[2], resVals[2])) // remove a record - keeper.RemoveValidator(ctx, validators[1].OperatorAddr) + validators[1].Status = sdk.Unbonded // First must set to Unbonded. + keeper.SetValidator(ctx, validators[1]) // ... + keeper.RemoveValidator(ctx, validators[1].OperatorAddr) // Now it can be removed. _, found = keeper.GetValidator(ctx, addrVals[1]) require.False(t, found) } diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index d76eb39c15..1a6ed6a641 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -173,6 +173,11 @@ func ErrConflictingRedelegation(codespace sdk.CodespaceType) sdk.Error { "conflicting redelegation from this source validator to this dest validator already exists, you must wait for it to finish") } +func ErrDelegatorShareExRateInvalid(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "cannot delegate to validators with invalid (zero) ex-rate") +} + func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided") } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 9301da3ece..ca56e0ea4a 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -392,6 +392,9 @@ func (v Validator) AddTokensFromDel(pool Pool, amount sdk.Int) (Validator, Pool, pool = pool.looseTokensToBonded(amountDec) } + if exRate.IsZero() { + panic("zero exRate should not happen") + } v.Tokens = v.Tokens.Add(amountDec) issuedShares := amountDec.Quo(exRate) v.DelegatorShares = v.DelegatorShares.Add(issuedShares) From 10e8e0312eeed9a450e7c49f4337427b9268b459 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 5 Nov 2018 03:14:00 -0800 Subject: [PATCH 52/61] Revert "enforcing @jaekwon mergemaster" This reverts commit 15c209324f56e99f1bf1b323a3057ec2d27fad8e. --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9d31a14f6a..dcdf7080b8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers -* @jaekwon +* @ebuchman @rigelrozanski @cwgoes # Precious documentation /docs/ @zramsay @jolesbi From 1d3a04a61c6777bdfe8b01c5bed7bd3819feb25f Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 6 Nov 2018 23:33:18 -0800 Subject: [PATCH 53/61] Switch gov proposal-queues to use iterators (#2638) * switched gov proposals queue to use iterators * update gov spec * update proposal.Equal * Amino api change * switched proposalID to uint64 * renamed Gov Procedures to Params * s/ActiveProposalQueueProposalKey/KeyActiveProposalQueueProposal/g * numLatestProposals -> Limit * fixed staking invariant breakage because of gov deposits * Send deposits to DepositedCoinsAccAddr or BurnedDepositCoinsAccAddr --- Gopkg.lock | 8 +- Gopkg.toml | 3 +- PENDING.md | 1 + client/lcd/lcd_test.go | 38 ++--- client/utils/rest.go | 14 ++ cmd/gaia/app/sim_test.go | 10 +- cmd/gaia/cli_test/cli_test.go | 13 +- docs/spec/governance/state.md | 51 ++---- docs/spec/governance/transactions.md | 20 +-- types/utils.go | 8 + x/gov/client/cli/tx.go | 44 ++--- x/gov/client/rest/rest.go | 26 +-- x/gov/depositsvotes.go | 4 +- x/gov/endblocker_test.go | 149 +++++++++-------- x/gov/errors.go | 8 +- x/gov/genesis.go | 40 ++--- x/gov/handler.go | 73 +++------ x/gov/keeper.go | 231 ++++++++++++--------------- x/gov/keeper_keys.go | 54 ++++++- x/gov/keeper_test.go | 61 +++---- x/gov/msgs.go | 8 +- x/gov/msgs_test.go | 6 +- x/gov/{procedures.go => params.go} | 6 +- x/gov/proposals.go | 44 +++-- x/gov/queryable.go | 28 ++-- x/gov/simulation/msgs.go | 45 ++---- x/gov/simulation/sim_test.go | 8 +- x/gov/tally.go | 6 +- 28 files changed, 488 insertions(+), 519 deletions(-) rename x/gov/{procedures.go => params.go} (91%) diff --git a/Gopkg.lock b/Gopkg.lock index f9be7bd97a..0e9681ed39 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -165,13 +165,12 @@ version = "v1.2.0" [[projects]] - digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" + digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", - "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -435,7 +434,7 @@ version = "v0.11.1" [[projects]] - digest = "1:92d7d1678577fd1a6f3348168cef87880bbc710ef5f4e9a1216f45c56567d734" + digest = "1:395820b381043b9d2204e181ddf0f9147397c4a7b8f5dc3162de4cfcddf4589a" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -501,7 +500,8 @@ "version", ] pruneopts = "UT" - revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" + revision = "03e42d2e3866f01a00625f608e3bbfaeb30690de" + version = "v0.26.1-rc0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index a66574ea62..5726008858 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -36,7 +36,7 @@ [[override]] name = "github.com/tendermint/tendermint" - revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" # TODO replace w/ 0.26.1 + version = "v0.26.1-rc0" # TODO replace w/ 0.26.1 ## deps without releases: @@ -84,4 +84,3 @@ [prune] go-tests = true unused-packages = true - diff --git a/PENDING.md b/PENDING.md index 11c13ea75f..232d28f631 100644 --- a/PENDING.md +++ b/PENDING.md @@ -40,6 +40,7 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia + - #2637 [x/gov] Switched inactive and active proposal queues to an iterator based queue * SDK - \#2573 [x/distribution] add accum invariance diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index d3140e5b8d..92167346d2 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -628,8 +628,8 @@ func TestSubmitProposal(t *testing.T) { require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + var proposalID uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -650,8 +650,8 @@ func TestDeposit(t *testing.T) { require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + var proposalID uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -684,8 +684,8 @@ func TestVote(t *testing.T) { require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + var proposalID uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -732,18 +732,18 @@ func TestProposalsQuery(t *testing.T) { // Addr1 proposes (and deposits) proposals #1 and #2 resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) - var proposalID1 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1) + var proposalID1 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1) tests.WaitForHeight(resultTx.Height+1, port) resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) - var proposalID2 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2) + var proposalID2 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 proposes (and deposits) proposals #3 resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5) - var proposalID3 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3) + var proposalID3 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 deposits on proposals #2 & #3 @@ -1230,7 +1230,7 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA // ============= Governance Module ================ -func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal { +func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var proposal gov.Proposal @@ -1239,7 +1239,7 @@ func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal { return proposal } -func getDeposits(t *testing.T, port string, proposalID int64) []gov.Deposit { +func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var deposits []gov.Deposit @@ -1248,7 +1248,7 @@ func getDeposits(t *testing.T, port string, proposalID int64) []gov.Deposit { return deposits } -func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.AccAddress) gov.Deposit { +func getDeposit(t *testing.T, port string, proposalID uint64, depositerAddr sdk.AccAddress) gov.Deposit { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositerAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var deposit gov.Deposit @@ -1257,7 +1257,7 @@ func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.A return deposit } -func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddress) gov.Vote { +func getVote(t *testing.T, port string, proposalID uint64, voterAddr sdk.AccAddress) gov.Vote { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var vote gov.Vote @@ -1266,7 +1266,7 @@ func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddre return vote } -func getVotes(t *testing.T, port string, proposalID int64) []gov.Vote { +func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var votes []gov.Vote @@ -1358,7 +1358,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA return results } -func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID int64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { +func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() @@ -1388,7 +1388,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk return results } -func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID int64) (resultTx ctypes.ResultBroadcastTxCommit) { +func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64) (resultTx ctypes.ResultBroadcastTxCommit) { // get the account to get the sequence acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() diff --git a/client/utils/rest.go b/client/utils/rest.go index e9d9485147..53c6186921 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -65,6 +65,20 @@ func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok return n, true } +// ParseUint64OrReturnBadRequest converts s to a uint64 value. +func ParseUint64OrReturnBadRequest(w http.ResponseWriter, s string) (n uint64, ok bool) { + var err error + + n, err = strconv.ParseUint(s, 10, 64) + if err != nil { + err := fmt.Errorf("'%s' is not a valid uint64", s) + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return n, false + } + + return n, true +} + // ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a // default value, defaultIfEmpty, if the string is empty. func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) { diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 0216616a88..431c5bc415 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -70,15 +70,15 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { // Random genesis states govGenesis := gov.GenesisState{ - StartingProposalID: int64(r.Intn(100)), - DepositProcedure: gov.DepositProcedure{ + StartingProposalID: uint64(r.Intn(100)), + DepositParams: gov.DepositParams{ MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))}, MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second, }, - VotingProcedure: gov.VotingProcedure{ + VotingParams: gov.VotingParams{ VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second, }, - TallyingProcedure: gov.TallyingProcedure{ + TallyParams: gov.TallyParams{ Threshold: sdk.NewDecWithPrec(5, 1), Veto: sdk.NewDecWithPrec(334, 3), GovernancePenalty: sdk.NewDecWithPrec(1, 2), @@ -166,7 +166,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { {50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, - {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, + {100, govsim.SimulateMsgDeposit(app.govKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountKeeper, app.stakeKeeper)}, {5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)}, {100, stakesim.SimulateMsgDelegate(app.accountKeeper, app.stakeKeeper)}, diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index c80b597c75..bc9151200c 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -5,13 +5,14 @@ package clitest import ( "encoding/json" "fmt" - "github.com/tendermint/tendermint/types" "io/ioutil" "os" "path" "path/filepath" "testing" + "github.com/tendermint/tendermint/types" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -348,7 +349,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64()) proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) - require.Equal(t, int64(1), proposal1.GetProposalID()) + require.Equal(t, uint64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") @@ -391,7 +392,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64()) proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) - require.Equal(t, int64(1), proposal1.GetProposalID()) + require.Equal(t, uint64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus()) voteStr := fmt.Sprintf("gaiacli tx vote %v", flags) @@ -413,12 +414,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) { tests.WaitForNextNBlocksTM(2, port) vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags)) - require.Equal(t, int64(1), vote.ProposalID) + require.Equal(t, uint64(1), vote.ProposalID) require.Equal(t, gov.OptionYes, vote.Option) votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags)) require.Len(t, votes, 1) - require.Equal(t, int64(1), votes[0].ProposalID) + require.Equal(t, uint64(1), votes[0].ProposalID) require.Equal(t, gov.OptionYes, votes[0].Option) proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "") @@ -438,7 +439,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { executeWrite(t, spStr, app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) - proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --limit=1 %v", flags), "") require.Equal(t, " 2 - Apples", proposalsQuery) } diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 5b577cec39..ca130bd4c3 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -96,10 +96,12 @@ type Proposal struct { Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit Deposits []Deposit // List of deposits on the proposal - SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included - Submitter sdk.Address // Address of the submitter + SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included + DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire + Submitter sdk.AccAddress // Address of the submitter - VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached + VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached + VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end. CurrentStatus ProposalStatus // Current status of the proposal YesVotes sdk.Dec @@ -134,46 +136,26 @@ For pseudocode purposes, here are the two function we will use to read or write **Store:** * `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the - `ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest - element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if - `CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is, - then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted - and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded. - After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated. + `ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals + that have reached the end of their voting period are processed. + To process a finished proposal, the application tallies the votes, compute the votes of + each validator and checks if every validator in the valdiator set have voted. + If the proposal is accepted, deposits are refunded. And the pseudocode for the `ProposalProcessingQueue`: ```go in EndBlock do - checkProposal() // First call of the recursive function - - - // Recursive function. First call in BeginBlock - func checkProposal() - proposalID = ProposalProcessingQueue.Peek() - if (proposalID == nil) - return + for finishedProposalID in GetAllFinishedProposalIDs(block.Time) + proposal = load(Governance, ) // proposal is a const key - proposal = load(Governance, ) // proposal is a const key - votingProcedure = load(GlobalParams, 'VotingProcedure') + validators = Keeper.getAllValidators() + tmpValMap := map(sdk.AccAddress)ValidatorGovInfo - if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) - - // End of voting period, tally - - ProposalProcessingQueue.pop() - validators = - - - Keeper.getAllValidators() - tmpValMap := map(sdk.Address)ValidatorGovInfo - - // Initiate mapping at 0. Validators that remain at 0 at the end of tally will be punished + // Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes for each validator in validators - tmpValMap(validator).Minus = 0 - - + tmpValMap(validator.OperatorAddr).Minus = 0 // Tally voterIterator = rangeQuery(Governance, ) //return all the addresses that voted on the proposal @@ -212,5 +194,4 @@ And the pseudocode for the `ProposalProcessingQueue`: proposal.CurrentStatus = ProposalStatusRejected store(Governance, , proposal) - checkProposal() ``` diff --git a/docs/spec/governance/transactions.md b/docs/spec/governance/transactions.md index 5ce91284fd..f3636facda 100644 --- a/docs/spec/governance/transactions.md +++ b/docs/spec/governance/transactions.md @@ -45,6 +45,8 @@ upon receiving txGovSubmitProposal from sender do if (txGovSubmitProposal.Type != ProposalTypePlainText) OR (txGovSubmitProposal.Type != ProposalTypeSoftwareUpgrade) sender.AtomBalance -= initialDeposit.Atoms + + depositProcedure = load(GlobalParams, 'DepositProcedure') proposalID = generate new proposalID proposal = NewProposal() @@ -53,27 +55,15 @@ upon receiving txGovSubmitProposal from sender do proposal.Description = txGovSubmitProposal.Description proposal.Type = txGovSubmitProposal.Type proposal.TotalDeposit = initialDeposit - proposal.SubmitBlock = CurrentBlock + proposal.SubmitTime = + proposal.DepositEndTime = .Add(depositProcedure.MaxDepositPeriod) proposal.Deposits.append({initialDeposit, sender}) proposal.Submitter = sender proposal.YesVotes = 0 proposal.NoVotes = 0 proposal.NoWithVetoVotes = 0 proposal.AbstainVotes = 0 - - depositProcedure = load(GlobalParams, 'DepositProcedure') - - if (initialDeposit < depositProcedure.MinDeposit) - // MinDeposit is not reached - - proposal.CurrentStatus = ProposalStatusOpen - - else - // MinDeposit is reached - - proposal.CurrentStatus = ProposalStatusActive - proposal.VotingStartBlock = CurrentBlock - ProposalProcessingQueue.push(proposalID) + proposal.CurrentStatus = ProposalStatusOpen store(Proposals, , proposal) // Store proposal in Proposals mapping return proposalID diff --git a/types/utils.go b/types/utils.go index 10ec854728..91cdcf9b03 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,6 +1,7 @@ package types import ( + "encoding/binary" "encoding/json" "time" @@ -36,6 +37,13 @@ func MustSortJSON(toSortJSON []byte) []byte { return js } +// Uint64ToBigEndian - marshals uint64 to a bigendian byte slice so it can be sorted +func Uint64ToBigEndian(i uint64) []byte { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, i) + return b +} + // Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info const SortableTimeFormat = "2006-01-02T15:04:05.000000000" diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 4b23002bd7..455bd58db5 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -21,17 +21,17 @@ import ( ) const ( - flagProposalID = "proposal-id" - flagTitle = "title" - flagDescription = "description" - flagProposalType = "type" - flagDeposit = "deposit" - flagVoter = "voter" - flagOption = "option" - flagDepositer = "depositer" - flagStatus = "status" - flagLatestProposalIDs = "latest" - flagProposal = "proposal" + flagProposalID = "proposal-id" + flagTitle = "title" + flagDescription = "description" + flagProposalType = "type" + flagDeposit = "deposit" + flagVoter = "voter" + flagOption = "option" + flagDepositer = "depositer" + flagStatus = "status" + flagNumLimit = "limit" + flagProposal = "proposal" ) type proposal struct { @@ -170,7 +170,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { return err } - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) amount, err := sdk.ParseCoins(viper.GetString(flagDeposit)) if err != nil { @@ -215,7 +215,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { return err } - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) option := viper.GetString(flagOption) byteVoteOption, err := gov.VoteOptionFromString(client.NormalizeVoteOption(option)) @@ -256,7 +256,7 @@ func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query details of a single proposal", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) params := gov.QueryProposalParams{ ProposalID: proposalID, @@ -291,10 +291,10 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { bechDepositerAddr := viper.GetString(flagDepositer) bechVoterAddr := viper.GetString(flagVoter) strProposalStatus := viper.GetString(flagStatus) - latestProposalsIDs := viper.GetInt64(flagLatestProposalIDs) + numLimit := uint64(viper.GetInt64(flagNumLimit)) params := gov.QueryProposalsParams{ - NumLatestProposals: latestProposalsIDs, + Limit: numLimit, } if len(bechDepositerAddr) != 0 { @@ -352,7 +352,7 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { }, } - cmd.Flags().String(flagLatestProposalIDs, "", "(optional) limit to latest [number] proposals. Defaults to all proposals") + cmd.Flags().String(flagNumLimit, "", "(optional) limit to latest [number] proposals. Defaults to all proposals") cmd.Flags().String(flagDepositer, "", "(optional) filter by proposals deposited on by depositer") cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") @@ -368,7 +368,7 @@ func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query details of a single vote", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) voterAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagVoter)) if err != nil { @@ -407,7 +407,7 @@ func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query votes on a proposal", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) params := gov.QueryVotesParams{ ProposalID: proposalID, @@ -440,7 +440,7 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query details of a deposit", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) depositerAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositer)) if err != nil { @@ -479,7 +479,7 @@ func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query deposits on a proposal", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) params := gov.QueryDepositsParams{ ProposalID: proposalID, @@ -511,7 +511,7 @@ func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Get the tally of a proposal vote", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := viper.GetInt64(flagProposalID) + proposalID := uint64(viper.GetInt64(flagProposalID)) params := gov.QueryTallyParams{ ProposalID: proposalID, diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 842edfdacb..f477f2ff9a 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -22,7 +22,7 @@ const ( RestDepositer = "depositer" RestVoter = "voter" RestProposalStatus = "status" - RestNumLatest = "latest" + RestNumLimit = "limit" storeName = "gov" ) @@ -104,7 +104,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -143,7 +143,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -188,7 +188,7 @@ func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -218,7 +218,7 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha vars := mux.Vars(r) strProposalID := vars[RestProposalID] - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -255,7 +255,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -319,7 +319,7 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -386,7 +386,7 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -416,7 +416,7 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) bechVoterAddr := r.URL.Query().Get(RestVoter) bechDepositerAddr := r.URL.Query().Get(RestDepositer) strProposalStatus := r.URL.Query().Get(RestProposalStatus) - strNumLatest := r.URL.Query().Get(RestNumLatest) + strNumLimit := r.URL.Query().Get(RestNumLimit) params := gov.QueryProposalsParams{} @@ -446,12 +446,12 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) } params.ProposalStatus = proposalStatus } - if len(strNumLatest) != 0 { - numLatest, ok := utils.ParseInt64OrReturnBadRequest(w, strNumLatest) + if len(strNumLimit) != 0 { + numLimit, ok := utils.ParseUint64OrReturnBadRequest(w, strNumLimit) if !ok { return } - params.NumLatestProposals = numLatest + params.Limit = numLimit } bz, err := cdc.MarshalJSON(params) @@ -484,7 +484,7 @@ func queryTallyOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - proposalID, ok := utils.ParseInt64OrReturnBadRequest(w, strProposalID) + proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index d1179023fa..7a6b043e63 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -11,7 +11,7 @@ import ( // Vote type Vote struct { Voter sdk.AccAddress `json:"voter"` // address of the voter - ProposalID int64 `json:"proposal_id"` // proposalID of the proposal + ProposalID uint64 `json:"proposal_id"` // proposalID of the proposal Option VoteOption `json:"option"` // option from OptionSet chosen by the voter } @@ -29,7 +29,7 @@ func (voteA Vote) Empty() bool { // Deposit type Deposit struct { Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer - ProposalID int64 `json:"proposal_id"` // proposalID of the proposal + ProposalID uint64 `json:"proposal_id"` // proposalID of the proposal Amount sdk.Coins `json:"amount"` // Deposit amount } diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 27eff15f64..535cf036a5 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -16,35 +16,40 @@ func TestTickExpiredDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositParams(ctx).MaxDepositPeriod) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() + EndBlocker(ctx, keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() } func TestTickMultipleExpiredDepositPeriod(t *testing.T) { @@ -53,49 +58,54 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(2) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res = govHandler(ctx, newProposalMsg2) require.True(t, res.IsOK()) newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) + newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader = ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(5) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() EndBlocker(ctx, keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() } func TestTickPassedDepositPeriod(t *testing.T) { @@ -104,45 +114,39 @@ func TestTickPassedDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + activeQueue := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) + var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - - EndBlocker(ctx, keeper) - - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) - + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() } func TestTickPassedVotingPeriod(t *testing.T) { @@ -152,17 +156,19 @@ func TestTickPassedVotingPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + activeQueue := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) + var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID) newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) @@ -172,24 +178,27 @@ func TestTickPassedVotingPeriod(t *testing.T) { res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(keeper.GetVotingParams(ctx).VotingPeriod) ctx = ctx.WithBlockHeader(newHeader) - require.True(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, activeQueue.Valid()) + var activeProposalID uint64 + keeper.cdc.UnmarshalBinaryLengthPrefixed(activeQueue.Value(), &activeProposalID) + require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, activeProposalID).GetStatus()) depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) depositsIterator.Close() - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) + activeQueue.Close() EndBlocker(ctx, keeper) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - depositsIterator = keeper.GetDeposits(ctx, proposalID) - require.False(t, depositsIterator.Valid()) - depositsIterator.Close() - require.Equal(t, StatusRejected, keeper.GetProposal(ctx, proposalID).GetStatus()) - require.True(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() } diff --git a/x/gov/errors.go b/x/gov/errors.go index cd1cf8a177..e803d080ea 100644 --- a/x/gov/errors.go +++ b/x/gov/errors.go @@ -26,19 +26,19 @@ const ( //---------------------------------------- // Error constructors -func ErrUnknownProposal(codespace sdk.CodespaceType, proposalID int64) sdk.Error { +func ErrUnknownProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error { return sdk.NewError(codespace, CodeUnknownProposal, fmt.Sprintf("Unknown proposal with id %d", proposalID)) } -func ErrInactiveProposal(codespace sdk.CodespaceType, proposalID int64) sdk.Error { +func ErrInactiveProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error { return sdk.NewError(codespace, CodeInactiveProposal, fmt.Sprintf("Inactive proposal with id %d", proposalID)) } -func ErrAlreadyActiveProposal(codespace sdk.CodespaceType, proposalID int64) sdk.Error { +func ErrAlreadyActiveProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error { return sdk.NewError(codespace, CodeAlreadyActiveProposal, fmt.Sprintf("Proposal %d has been already active", proposalID)) } -func ErrAlreadyFinishedProposal(codespace sdk.CodespaceType, proposalID int64) sdk.Error { +func ErrAlreadyFinishedProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error { return sdk.NewError(codespace, CodeAlreadyFinishedProposal, fmt.Sprintf("Proposal %d has already passed its voting period", proposalID)) } diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 58273c8e84..db35f68c8c 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -8,18 +8,18 @@ import ( // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - StartingProposalID int64 `json:"starting_proposalID"` - DepositProcedure DepositProcedure `json:"deposit_period"` - VotingProcedure VotingProcedure `json:"voting_period"` - TallyingProcedure TallyingProcedure `json:"tallying_procedure"` + StartingProposalID uint64 `json:"starting_proposalID"` + DepositParams DepositParams `json:"deposit_params"` + VotingParams VotingParams `json:"voting_params"` + TallyParams TallyParams `json:"tally_params"` } -func NewGenesisState(startingProposalID int64, dp DepositProcedure, vp VotingProcedure, tp TallyingProcedure) GenesisState { +func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState { return GenesisState{ StartingProposalID: startingProposalID, - DepositProcedure: dp, - VotingProcedure: vp, - TallyingProcedure: tp, + DepositParams: dp, + VotingParams: vp, + TallyParams: tp, } } @@ -27,14 +27,14 @@ func NewGenesisState(startingProposalID int64, dp DepositProcedure, vp VotingPro func DefaultGenesisState() GenesisState { return GenesisState{ StartingProposalID: 1, - DepositProcedure: DepositProcedure{ + DepositParams: DepositParams{ MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", 10)}, MaxDepositPeriod: time.Duration(172800) * time.Second, }, - VotingProcedure: VotingProcedure{ + VotingParams: VotingParams{ VotingPeriod: time.Duration(172800) * time.Second, }, - TallyingProcedure: TallyingProcedure{ + TallyParams: TallyParams{ Threshold: sdk.NewDecWithPrec(5, 1), Veto: sdk.NewDecWithPrec(334, 3), GovernancePenalty: sdk.NewDecWithPrec(1, 2), @@ -49,22 +49,22 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { // TODO: Handle this with #870 panic(err) } - k.setDepositProcedure(ctx, data.DepositProcedure) - k.setVotingProcedure(ctx, data.VotingProcedure) - k.setTallyingProcedure(ctx, data.TallyingProcedure) + k.setDepositParams(ctx, data.DepositParams) + k.setVotingParams(ctx, data.VotingParams) + k.setTallyParams(ctx, data.TallyParams) } // WriteGenesis - output genesis parameters func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState { startingProposalID, _ := k.getNewProposalID(ctx) - depositProcedure := k.GetDepositProcedure(ctx) - votingProcedure := k.GetVotingProcedure(ctx) - tallyingProcedure := k.GetTallyingProcedure(ctx) + depositParams := k.GetDepositParams(ctx) + votingParams := k.GetVotingParams(ctx) + tallyParams := k.GetTallyParams(ctx) return GenesisState{ StartingProposalID: startingProposalID, - DepositProcedure: depositProcedure, - VotingProcedure: votingProcedure, - TallyingProcedure: tallyingProcedure, + DepositParams: depositParams, + VotingParams: votingParams, + TallyParams: tallyParams, } } diff --git a/x/gov/handler.go b/x/gov/handler.go index dc1dbfb10e..279c4cc6e6 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -33,7 +33,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos return err.Result() } - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(proposal.GetProposalID()) + proposalIDBytes := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal.GetProposalID()) resTags := sdk.NewTags( tags.Action, tags.ActionSubmitProposal, @@ -102,40 +102,35 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags = sdk.NewTags() - // Delete proposals that haven't met minDeposit - for shouldPopInactiveProposalQueue(ctx, keeper) { - inactiveProposal := keeper.InactiveProposalQueuePop(ctx) - if inactiveProposal.GetStatus() != StatusDepositPeriod { - continue - } + inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + for ; inactiveIterator.Valid(); inactiveIterator.Next() { + var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) + inactiveProposal := keeper.GetProposal(ctx, proposalID) + keeper.RefundDeposits(ctx, proposalID) + keeper.DeleteProposal(ctx, proposalID) - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(inactiveProposal.GetProposalID()) - keeper.DeleteProposal(ctx, inactiveProposal) resTags = resTags.AppendTag(tags.Action, tags.ActionProposalDropped) - resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) logger.Info( - fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %v steak); deleted", + fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted", inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), - keeper.GetDepositProcedure(ctx).MinDeposit.AmountOf("steak"), - inactiveProposal.GetTotalDeposit().AmountOf("steak"), + keeper.GetDepositParams(ctx).MinDeposit, + inactiveProposal.GetTotalDeposit(), ), ) } + inactiveIterator.Close() - // Check if earliest Active Proposal ended voting period yet - for shouldPopActiveProposalQueue(ctx, keeper) { - activeProposal := keeper.ActiveProposalQueuePop(ctx) - - proposalStartTime := activeProposal.GetVotingStartTime() - votingPeriod := keeper.GetVotingProcedure(ctx).VotingPeriod - if ctx.BlockHeader().Time.Before(proposalStartTime.Add(votingPeriod)) { - continue - } - + activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + for ; activeIterator.Valid(); activeIterator.Next() { + var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) + activeProposal := keeper.GetProposal(ctx, proposalID) passes, tallyResults := tally(ctx, keeper, activeProposal) - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(activeProposal.GetProposalID()) + var action []byte if passes { keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) @@ -149,37 +144,15 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { activeProposal.SetTallyResult(tallyResults) keeper.SetProposal(ctx, activeProposal) + keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.GetVotingEndTime(), activeProposal.GetProposalID()) + logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) resTags = resTags.AppendTag(tags.Action, action) - resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) } + activeIterator.Close() return resTags } -func shouldPopInactiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { - depositProcedure := keeper.GetDepositProcedure(ctx) - peekProposal := keeper.InactiveProposalQueuePeek(ctx) - - if peekProposal == nil { - return false - } else if peekProposal.GetStatus() != StatusDepositPeriod { - return true - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetSubmitTime().Add(depositProcedure.MaxDepositPeriod)) { - return true - } - return false -} - -func shouldPopActiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { - votingProcedure := keeper.GetVotingProcedure(ctx) - peekProposal := keeper.ActiveProposalQueuePeek(ctx) - - if peekProposal == nil { - return false - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetVotingStartTime().Add(votingProcedure.VotingPeriod)) { - return true - } - return false -} diff --git a/x/gov/keeper.go b/x/gov/keeper.go index a3e3cda253..48d9000d81 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -1,10 +1,14 @@ package gov import ( + "time" + codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/tendermint/tendermint/crypto" ) // Parameter store default namestore @@ -14,17 +18,20 @@ const ( // Parameter store key var ( - ParamStoreKeyDepositProcedure = []byte("depositprocedure") - ParamStoreKeyVotingProcedure = []byte("votingprocedure") - ParamStoreKeyTallyingProcedure = []byte("tallyingprocedure") + ParamStoreKeyDepositParams = []byte("depositparams") + ParamStoreKeyVotingParams = []byte("votingparams") + ParamStoreKeyTallyParams = []byte("tallyparams") + + DepositedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) + BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) ) // Type declaration for parameters func ParamTypeTable() params.TypeTable { return params.NewTypeTable( - ParamStoreKeyDepositProcedure, DepositProcedure{}, - ParamStoreKeyVotingProcedure, VotingProcedure{}, - ParamStoreKeyTallyingProcedure, TallyingProcedure{}, + ParamStoreKeyDepositParams, DepositParams{}, + ParamStoreKeyVotingParams, VotingParams{}, + ParamStoreKeyTallyParams, TallyParams{}, ) } @@ -92,13 +99,17 @@ func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description TotalDeposit: sdk.Coins{}, SubmitTime: ctx.BlockHeader().Time, } + + depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod + proposal.SetDepositEndTime(proposal.GetSubmitTime().Add(depositPeriod)) + keeper.SetProposal(ctx, proposal) - keeper.InactiveProposalQueuePush(ctx, proposal) + keeper.InsertInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposalID) return proposal } // Get Proposal from store by ProposalID -func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID int64) Proposal { +func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) Proposal { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyProposal(proposalID)) if bz == nil { @@ -119,13 +130,16 @@ func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) { } // Implements sdk.AccountKeeper. -func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { +func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) - store.Delete(KeyProposal(proposal.GetProposalID())) + proposal := keeper.GetProposal(ctx, proposalID) + keeper.RemoveFromInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposalID) + keeper.RemoveFromActiveProposalQueue(ctx, proposal.GetVotingEndTime(), proposalID) + store.Delete(KeyProposal(proposalID)) } // Get Proposal from store by ProposalID -func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal { +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal { maxProposalID, err := keeper.peekCurrentProposalID(ctx) if err != nil { @@ -134,7 +148,7 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr matchingProposals := []Proposal{} - if numLatest <= 0 { + if numLatest == 0 { numLatest = maxProposalID } @@ -169,7 +183,7 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr return matchingProposals } -func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk.Error { +func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID uint64) sdk.Error { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) if bz != nil { @@ -181,7 +195,7 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk } // Get the last used proposal ID -func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { +func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID uint64) { proposalID, err := keeper.peekCurrentProposalID(ctx) if err != nil { return 0 @@ -191,11 +205,11 @@ func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { } // Gets the next available ProposalID and increments it -func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { +func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) if bz == nil { - return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") + return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID + 1) @@ -204,11 +218,11 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd } // Peeks the next available ProposalID without incrementing it -func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { +func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) if bz == nil { - return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") + return 0, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) return proposalID, nil @@ -216,58 +230,62 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { proposal.SetVotingStartTime(ctx.BlockHeader().Time) + votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod + proposal.SetVotingEndTime(proposal.GetVotingStartTime().Add(votingPeriod)) proposal.SetStatus(StatusVotingPeriod) keeper.SetProposal(ctx, proposal) - keeper.ActiveProposalQueuePush(ctx, proposal) + + keeper.RemoveFromInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposal.GetProposalID()) + keeper.InsertActiveProposalQueue(ctx, proposal.GetVotingEndTime(), proposal.GetProposalID()) } // ===================================================== -// Procedures +// Params -// Returns the current Deposit Procedure from the global param store +// Returns the current DepositParams from the global param store // nolint: errcheck -func (keeper Keeper) GetDepositProcedure(ctx sdk.Context) DepositProcedure { - var depositProcedure DepositProcedure - keeper.paramSpace.Get(ctx, ParamStoreKeyDepositProcedure, &depositProcedure) - return depositProcedure +func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams { + var depositParams DepositParams + keeper.paramSpace.Get(ctx, ParamStoreKeyDepositParams, &depositParams) + return depositParams } // Returns the current Voting Procedure from the global param store // nolint: errcheck -func (keeper Keeper) GetVotingProcedure(ctx sdk.Context) VotingProcedure { - var votingProcedure VotingProcedure - keeper.paramSpace.Get(ctx, ParamStoreKeyVotingProcedure, &votingProcedure) - return votingProcedure +func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams { + var votingParams VotingParams + keeper.paramSpace.Get(ctx, ParamStoreKeyVotingParams, &votingParams) + return votingParams } // Returns the current Tallying Procedure from the global param store // nolint: errcheck -func (keeper Keeper) GetTallyingProcedure(ctx sdk.Context) TallyingProcedure { - var tallyingProcedure TallyingProcedure - keeper.paramSpace.Get(ctx, ParamStoreKeyTallyingProcedure, &tallyingProcedure) - return tallyingProcedure +func (keeper Keeper) GetTallyParams(ctx sdk.Context) TallyParams { + var tallyParams TallyParams + keeper.paramSpace.Get(ctx, ParamStoreKeyTallyParams, &tallyParams) + return tallyParams } // nolint: errcheck -func (keeper Keeper) setDepositProcedure(ctx sdk.Context, depositProcedure DepositProcedure) { - keeper.paramSpace.Set(ctx, ParamStoreKeyDepositProcedure, &depositProcedure) +func (keeper Keeper) setDepositParams(ctx sdk.Context, depositParams DepositParams) { + keeper.paramSpace.Set(ctx, ParamStoreKeyDepositParams, &depositParams) } // nolint: errcheck -func (keeper Keeper) setVotingProcedure(ctx sdk.Context, votingProcedure VotingProcedure) { - keeper.paramSpace.Set(ctx, ParamStoreKeyVotingProcedure, &votingProcedure) +func (keeper Keeper) setVotingParams(ctx sdk.Context, votingParams VotingParams) { + keeper.paramSpace.Set(ctx, ParamStoreKeyVotingParams, &votingParams) } // nolint: errcheck -func (keeper Keeper) setTallyingProcedure(ctx sdk.Context, tallyingProcedure TallyingProcedure) { - keeper.paramSpace.Set(ctx, ParamStoreKeyTallyingProcedure, &tallyingProcedure) +func (keeper Keeper) setTallyParams(ctx sdk.Context, tallyParams TallyParams) { + keeper.paramSpace.Set(ctx, ParamStoreKeyTallyParams, &tallyParams) } // ===================================================== // Votes // Adds a vote on a specific proposal -func (keeper Keeper) AddVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress, option VoteOption) sdk.Error { +func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option VoteOption) sdk.Error { proposal := keeper.GetProposal(ctx, proposalID) if proposal == nil { return ErrUnknownProposal(keeper.codespace, proposalID) @@ -291,7 +309,7 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID int64, voterAddr sdk.Ac } // Gets the vote of a specific voter on a specific proposal -func (keeper Keeper) GetVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress) (Vote, bool) { +func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (Vote, bool) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyVote(proposalID, voterAddr)) if bz == nil { @@ -302,19 +320,19 @@ func (keeper Keeper) GetVote(ctx sdk.Context, proposalID int64, voterAddr sdk.Ac return vote, true } -func (keeper Keeper) setVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress, vote Vote) { +func (keeper Keeper) setVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, vote Vote) { store := ctx.KVStore(keeper.storeKey) bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote) store.Set(KeyVote(proposalID, voterAddr), bz) } // Gets all the votes on a specific proposal -func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID int64) sdk.Iterator { +func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID uint64) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) return sdk.KVStorePrefixIterator(store, KeyVotesSubspace(proposalID)) } -func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress) { +func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { store := ctx.KVStore(keeper.storeKey) store.Delete(KeyVote(proposalID, voterAddr)) } @@ -323,7 +341,7 @@ func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID int64, voterAddr sdk // Deposits // Gets the deposit of a specific depositer on a specific proposal -func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID int64, depositerAddr sdk.AccAddress) (Deposit, bool) { +func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress) (Deposit, bool) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyDeposit(proposalID, depositerAddr)) if bz == nil { @@ -334,7 +352,7 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID int64, depositerAddr return deposit, true } -func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID int64, depositerAddr sdk.AccAddress, deposit Deposit) { +func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress, deposit Deposit) { store := ctx.KVStore(keeper.storeKey) bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit) store.Set(KeyDeposit(proposalID, depositerAddr), bz) @@ -342,7 +360,7 @@ func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID int64, depositerAddr // Adds or updates a deposit of a specific depositer on a specific proposal // Activates voting period when appropriate -func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) { +func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) { // Checks to see if proposal exists proposal := keeper.GetProposal(ctx, proposalID) if proposal == nil { @@ -354,8 +372,8 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false } - // Subtract coins from depositer's account - _, _, err := keeper.ck.SubtractCoins(ctx, depositerAddr, depositAmount) + // Send coins from depositer's account to DepositedCoinsAccAddr account + _, err := keeper.ck.SendCoins(ctx, depositerAddr, DepositedCoinsAccAddr, depositAmount) if err != nil { return err, false } @@ -367,7 +385,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr // Check if deposit tipped proposal into voting period // Active voting period if so activatedVotingPeriod := false - if proposal.GetStatus() == StatusDepositPeriod && proposal.GetTotalDeposit().IsGTE(keeper.GetDepositProcedure(ctx).MinDeposit) { + if proposal.GetStatus() == StatusDepositPeriod && proposal.GetTotalDeposit().IsGTE(keeper.GetDepositParams(ctx).MinDeposit) { keeper.activateVotingPeriod(ctx, proposal) activatedVotingPeriod = true } @@ -386,13 +404,13 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr } // Gets all the deposits on a specific proposal -func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID int64) sdk.Iterator { +func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) return sdk.KVStorePrefixIterator(store, KeyDepositsSubspace(proposalID)) } // Returns and deletes all the deposits on a specific proposal -func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID int64) { +func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) depositsIterator := keeper.GetDeposits(ctx, proposalID) @@ -400,7 +418,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID int64) { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) - _, _, err := keeper.ck.AddCoins(ctx, deposit.Depositer, deposit.Amount) + _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositer, deposit.Amount) if err != nil { panic("should not happen") } @@ -412,11 +430,19 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID int64) { } // Deletes all the deposits on a specific proposal without refunding them -func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID int64) { +func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) depositsIterator := keeper.GetDeposits(ctx, proposalID) for ; depositsIterator.Valid(); depositsIterator.Next() { + deposit := &Deposit{} + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) + + _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount) + if err != nil { + panic("should not happen") + } + store.Delete(depositsIterator.Key()) } @@ -426,93 +452,40 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID int64) { // ===================================================== // ProposalQueues -func (keeper Keeper) getActiveProposalQueue(ctx sdk.Context) ProposalQueue { +// Returns an iterator for all the proposals in the Active Queue that expire by endTime +func (keeper Keeper) ActiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyActiveProposalQueue) - if bz == nil { - return nil - } - - var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) - - return proposalQueue + return store.Iterator(PrefixActiveProposalQueue, sdk.PrefixEndBytes(PrefixActiveProposalQueueTime(endTime))) } -func (keeper Keeper) setActiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { +// Inserts a ProposalID into the active proposal queue at endTime +func (keeper Keeper) InsertActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) - store.Set(KeyActiveProposalQueue, bz) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID) + store.Set(KeyActiveProposalQueueProposal(endTime, proposalID), bz) } -// Return the Proposal at the front of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePeek(ctx sdk.Context) Proposal { - proposalQueue := keeper.getActiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - return keeper.GetProposal(ctx, proposalQueue[0]) -} - -// Remove and return a Proposal from the front of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePop(ctx sdk.Context) Proposal { - proposalQueue := keeper.getActiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - frontElement, proposalQueue := proposalQueue[0], proposalQueue[1:] - keeper.setActiveProposalQueue(ctx, proposalQueue) - return keeper.GetProposal(ctx, frontElement) -} - -// Add a proposalID to the back of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePush(ctx sdk.Context, proposal Proposal) { - proposalQueue := append(keeper.getActiveProposalQueue(ctx), proposal.GetProposalID()) - keeper.setActiveProposalQueue(ctx, proposalQueue) -} - -func (keeper Keeper) getInactiveProposalQueue(ctx sdk.Context) ProposalQueue { +// removes a proposalID from the Active Proposal Queue +func (keeper Keeper) RemoveFromActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyInactiveProposalQueue) - if bz == nil { - return nil - } - - var proposalQueue ProposalQueue - - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) - - return proposalQueue + store.Delete(KeyActiveProposalQueueProposal(endTime, proposalID)) } -func (keeper Keeper) setInactiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { +// Returns an iterator for all the proposals in the Inactive Queue that expire by endTime +func (keeper Keeper) InactiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) - store.Set(KeyInactiveProposalQueue, bz) + return store.Iterator(PrefixInactiveProposalQueue, sdk.PrefixEndBytes(PrefixInactiveProposalQueueTime(endTime))) } -// Return the Proposal at the front of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePeek(ctx sdk.Context) Proposal { - proposalQueue := keeper.getInactiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - return keeper.GetProposal(ctx, proposalQueue[0]) +// Inserts a ProposalID into the inactive proposal queue at endTime +func (keeper Keeper) InsertInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) { + store := ctx.KVStore(keeper.storeKey) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID) + store.Set(KeyInactiveProposalQueueProposal(endTime, proposalID), bz) } -// Remove and return a Proposal from the front of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePop(ctx sdk.Context) Proposal { - proposalQueue := keeper.getInactiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - frontElement, proposalQueue := proposalQueue[0], proposalQueue[1:] - keeper.setInactiveProposalQueue(ctx, proposalQueue) - return keeper.GetProposal(ctx, frontElement) -} - -// Add a proposalID to the back of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePush(ctx sdk.Context, proposal Proposal) { - proposalQueue := append(keeper.getInactiveProposalQueue(ctx), proposal.GetProposalID()) - keeper.setInactiveProposalQueue(ctx, proposalQueue) +// removes a proposalID from the Inactive Proposal Queue +func (keeper Keeper) RemoveFromInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) { + store := ctx.KVStore(keeper.storeKey) + store.Delete(KeyInactiveProposalQueueProposal(endTime, proposalID)) } diff --git a/x/gov/keeper_keys.go b/x/gov/keeper_keys.go index 7b1bf43f2f..8a3324fd18 100644 --- a/x/gov/keeper_keys.go +++ b/x/gov/keeper_keys.go @@ -1,7 +1,9 @@ package gov import ( + "bytes" "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -10,32 +12,68 @@ import ( // Key for getting a the next available proposalID from the store var ( - KeyNextProposalID = []byte("newProposalID") - KeyActiveProposalQueue = []byte("activeProposalQueue") - KeyInactiveProposalQueue = []byte("inactiveProposalQueue") + KeyDelimiter = []byte(":") + + KeyNextProposalID = []byte("newProposalID") + PrefixActiveProposalQueue = []byte("activeProposalQueue") + PrefixInactiveProposalQueue = []byte("inactiveProposalQueue") ) // Key for getting a specific proposal from the store -func KeyProposal(proposalID int64) []byte { +func KeyProposal(proposalID uint64) []byte { return []byte(fmt.Sprintf("proposals:%d", proposalID)) } // Key for getting a specific deposit from the store -func KeyDeposit(proposalID int64, depositerAddr sdk.AccAddress) []byte { +func KeyDeposit(proposalID uint64, depositerAddr sdk.AccAddress) []byte { return []byte(fmt.Sprintf("deposits:%d:%d", proposalID, depositerAddr)) } // Key for getting a specific vote from the store -func KeyVote(proposalID int64, voterAddr sdk.AccAddress) []byte { +func KeyVote(proposalID uint64, voterAddr sdk.AccAddress) []byte { return []byte(fmt.Sprintf("votes:%d:%d", proposalID, voterAddr)) } // Key for getting all deposits on a proposal from the store -func KeyDepositsSubspace(proposalID int64) []byte { +func KeyDepositsSubspace(proposalID uint64) []byte { return []byte(fmt.Sprintf("deposits:%d:", proposalID)) } // Key for getting all votes on a proposal from the store -func KeyVotesSubspace(proposalID int64) []byte { +func KeyVotesSubspace(proposalID uint64) []byte { return []byte(fmt.Sprintf("votes:%d:", proposalID)) } + +// Returns the key for a proposalID in the activeProposalQueue +func PrefixActiveProposalQueueTime(endTime time.Time) []byte { + return bytes.Join([][]byte{ + PrefixActiveProposalQueue, + sdk.FormatTimeBytes(endTime), + }, KeyDelimiter) +} + +// Returns the key for a proposalID in the activeProposalQueue +func KeyActiveProposalQueueProposal(endTime time.Time, proposalID uint64) []byte { + return bytes.Join([][]byte{ + PrefixActiveProposalQueue, + sdk.FormatTimeBytes(endTime), + sdk.Uint64ToBigEndian(proposalID), + }, KeyDelimiter) +} + +// Returns the key for a proposalID in the activeProposalQueue +func PrefixInactiveProposalQueueTime(endTime time.Time) []byte { + return bytes.Join([][]byte{ + PrefixInactiveProposalQueue, + sdk.FormatTimeBytes(endTime), + }, KeyDelimiter) +} + +// Returns the key for a proposalID in the activeProposalQueue +func KeyInactiveProposalQueueProposal(endTime time.Time, proposalID uint64) []byte { + return bytes.Join([][]byte{ + PrefixInactiveProposalQueue, + sdk.FormatTimeBytes(endTime), + sdk.Uint64ToBigEndian(proposalID), + }, KeyDelimiter) +} diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 0152c50bbd..2ff1344b78 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -36,7 +36,7 @@ func TestIncrementProposalNumber(t *testing.T) { keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposal6 := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) - require.Equal(t, int64(6), proposal6.GetProposalID()) + require.Equal(t, uint64(6), proposal6.GetProposalID()) } func TestActivateVotingPeriod(t *testing.T) { @@ -47,12 +47,17 @@ func TestActivateVotingPeriod(t *testing.T) { proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) require.True(t, proposal.GetVotingStartTime().Equal(time.Time{})) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) keeper.activateVotingPeriod(ctx, proposal) require.True(t, proposal.GetVotingStartTime().Equal(ctx.BlockHeader().Time)) - require.Equal(t, proposal.GetProposalID(), keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) + + activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.GetVotingEndTime()) + require.True(t, activeIterator.Valid()) + var proposalID uint64 + keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + activeIterator.Close() } func TestDeposits(t *testing.T) { @@ -79,7 +84,6 @@ func TestDeposits(t *testing.T) { deposit, found := keeper.GetDeposit(ctx, proposalID, addrs[1]) require.False(t, found) require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(time.Time{})) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) // Check first deposit err, votingStarted := keeper.AddDeposit(ctx, proposalID, addrs[0], fourSteak) @@ -116,8 +120,6 @@ func TestDeposits(t *testing.T) { // Check that proposal moved to voting period require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(ctx.BlockHeader().Time)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.Equal(t, proposalID, keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) // Test deposit iterator depositsIterator := keeper.GetDeposits(ctx, proposalID) @@ -207,44 +209,21 @@ func TestProposalQueues(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) mapp.InitChainer(ctx, abci.RequestInitChain{}) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - // create test proposals proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) - proposal2 := keeper.NewTextProposal(ctx, "Test2", "description", ProposalTypeText) - proposal3 := keeper.NewTextProposal(ctx, "Test3", "description", ProposalTypeText) - proposal4 := keeper.NewTextProposal(ctx, "Test4", "description", ProposalTypeText) - // test pushing to inactive proposal queue - keeper.InactiveProposalQueuePush(ctx, proposal) - keeper.InactiveProposalQueuePush(ctx, proposal2) - keeper.InactiveProposalQueuePush(ctx, proposal3) - keeper.InactiveProposalQueuePush(ctx, proposal4) + inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, proposal.GetDepositEndTime()) + require.True(t, inactiveIterator.Valid()) + var proposalID uint64 + keeper.cdc.UnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + inactiveIterator.Close() - // test peeking and popping from inactive proposal queue - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal4.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal4.GetProposalID()) + keeper.activateVotingPeriod(ctx, proposal) - // test pushing to active proposal queue - keeper.ActiveProposalQueuePush(ctx, proposal) - keeper.ActiveProposalQueuePush(ctx, proposal2) - keeper.ActiveProposalQueuePush(ctx, proposal3) - keeper.ActiveProposalQueuePush(ctx, proposal4) - - // test peeking and popping from active proposal queue - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal4.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal4.GetProposalID()) + activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.GetVotingEndTime()) + require.True(t, activeIterator.Valid()) + keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + activeIterator.Close() } diff --git a/x/gov/msgs.go b/x/gov/msgs.go index 847c04ab0d..b8325d81ca 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -84,12 +84,12 @@ func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress { //----------------------------------------------------------- // MsgDeposit type MsgDeposit struct { - ProposalID int64 `json:"proposal_id"` // ID of the proposal + ProposalID uint64 `json:"proposal_id"` // ID of the proposal Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } -func NewMsgDeposit(depositer sdk.AccAddress, proposalID int64, amount sdk.Coins) MsgDeposit { +func NewMsgDeposit(depositer sdk.AccAddress, proposalID uint64, amount sdk.Coins) MsgDeposit { return MsgDeposit{ ProposalID: proposalID, Depositer: depositer, @@ -145,12 +145,12 @@ func (msg MsgDeposit) GetSigners() []sdk.AccAddress { //----------------------------------------------------------- // MsgVote type MsgVote struct { - ProposalID int64 `json:"proposal_id"` // ID of the proposal + ProposalID uint64 `json:"proposal_id"` // ID of the proposal Voter sdk.AccAddress `json:"voter"` // address of the voter Option VoteOption `json:"option"` // option from OptionSet chosen by the voter } -func NewMsgVote(voter sdk.AccAddress, proposalID int64, option VoteOption) MsgVote { +func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) MsgVote { return MsgVote{ ProposalID: proposalID, Voter: voter, diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index 6d2b8bb388..bdc273dcd6 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -53,13 +53,12 @@ func TestMsgSubmitProposal(t *testing.T) { func TestMsgDeposit(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) tests := []struct { - proposalID int64 + proposalID uint64 depositerAddr sdk.AccAddress depositAmount sdk.Coins expectPass bool }{ {0, addrs[0], coinsPos, true}, - {-1, addrs[0], coinsPos, false}, {1, sdk.AccAddress{}, coinsPos, false}, {1, addrs[0], coinsZero, true}, {1, addrs[0], coinsNeg, false}, @@ -80,13 +79,12 @@ func TestMsgDeposit(t *testing.T) { func TestMsgVote(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) tests := []struct { - proposalID int64 + proposalID uint64 voterAddr sdk.AccAddress option VoteOption expectPass bool }{ {0, addrs[0], OptionYes, true}, - {-1, addrs[0], OptionYes, false}, {0, sdk.AccAddress{}, OptionYes, false}, {0, addrs[0], OptionNo, true}, {0, addrs[0], OptionNoWithVeto, true}, diff --git a/x/gov/procedures.go b/x/gov/params.go similarity index 91% rename from x/gov/procedures.go rename to x/gov/params.go index e453add791..4d8126d141 100644 --- a/x/gov/procedures.go +++ b/x/gov/params.go @@ -7,19 +7,19 @@ import ( ) // Procedure around Deposits for governance -type DepositProcedure struct { +type DepositParams struct { MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period. MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } // Procedure around Tallying votes in governance -type TallyingProcedure struct { +type TallyParams struct { Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote } // Procedure around Voting in governance -type VotingProcedure struct { +type VotingParams struct { VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period. } diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 9d1ba860ad..e943fe1169 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -13,8 +13,8 @@ import ( //----------------------------------------------------------- // Proposal interface type Proposal interface { - GetProposalID() int64 - SetProposalID(int64) + GetProposalID() uint64 + SetProposalID(uint64) GetTitle() string SetTitle(string) @@ -34,11 +34,17 @@ type Proposal interface { GetSubmitTime() time.Time SetSubmitTime(time.Time) + GetDepositEndTime() time.Time + SetDepositEndTime(time.Time) + GetTotalDeposit() sdk.Coins SetTotalDeposit(sdk.Coins) GetVotingStartTime() time.Time SetVotingStartTime(time.Time) + + GetVotingEndTime() time.Time + SetVotingEndTime(time.Time) } // checks if two proposals are equal @@ -50,8 +56,10 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool { proposalA.GetStatus() == proposalB.GetStatus() && proposalA.GetTallyResult().Equals(proposalB.GetTallyResult()) && proposalA.GetSubmitTime().Equal(proposalB.GetSubmitTime()) && + proposalA.GetDepositEndTime().Equal(proposalB.GetDepositEndTime()) && proposalA.GetTotalDeposit().IsEqual(proposalB.GetTotalDeposit()) && - proposalA.GetVotingStartTime().Equal(proposalB.GetVotingStartTime()) { + proposalA.GetVotingStartTime().Equal(proposalB.GetVotingStartTime()) && + proposalA.GetVotingEndTime().Equal(proposalB.GetVotingEndTime()) { return true } return false @@ -60,7 +68,7 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool { //----------------------------------------------------------- // Text Proposals type TextProposal struct { - ProposalID int64 `json:"proposal_id"` // ID of the proposal + ProposalID uint64 `json:"proposal_id"` // ID of the proposal Title string `json:"title"` // Title of the proposal Description string `json:"description"` // Description of the proposal ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} @@ -68,18 +76,20 @@ type TextProposal struct { Status ProposalStatus `json:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected} TallyResult TallyResult `json:"tally_result"` // Result of Tallys - SubmitTime time.Time `json:"submit_time"` // Height of the block where TxGovSubmitProposal was included - TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit + SubmitTime time.Time `json:"submit_time"` // Time of the block where TxGovSubmitProposal was included + DepositEndTime time.Time `json:"deposit_end_time"` // Time that the Proposal would expire if deposit amount isn't met + TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit - VotingStartTime time.Time `json:"voting_start_time"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingStartTime time.Time `json:"voting_start_time"` // Time of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingEndTime time.Time `json:"voting_end_time"` // Time that the VotingPeriod for this proposal will end and votes will be tallied } // Implements Proposal Interface var _ Proposal = (*TextProposal)(nil) // nolint -func (tp TextProposal) GetProposalID() int64 { return tp.ProposalID } -func (tp *TextProposal) SetProposalID(proposalID int64) { tp.ProposalID = proposalID } +func (tp TextProposal) GetProposalID() uint64 { return tp.ProposalID } +func (tp *TextProposal) SetProposalID(proposalID uint64) { tp.ProposalID = proposalID } func (tp TextProposal) GetTitle() string { return tp.Title } func (tp *TextProposal) SetTitle(title string) { tp.Title = title } func (tp TextProposal) GetDescription() string { return tp.Description } @@ -92,16 +102,24 @@ func (tp TextProposal) GetTallyResult() TallyResult { return tp.T func (tp *TextProposal) SetTallyResult(tallyResult TallyResult) { tp.TallyResult = tallyResult } func (tp TextProposal) GetSubmitTime() time.Time { return tp.SubmitTime } func (tp *TextProposal) SetSubmitTime(submitTime time.Time) { tp.SubmitTime = submitTime } -func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } -func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } -func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } +func (tp TextProposal) GetDepositEndTime() time.Time { return tp.DepositEndTime } +func (tp *TextProposal) SetDepositEndTime(depositEndTime time.Time) { + tp.DepositEndTime = depositEndTime +} +func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } +func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } +func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } func (tp *TextProposal) SetVotingStartTime(votingStartTime time.Time) { tp.VotingStartTime = votingStartTime } +func (tp TextProposal) GetVotingEndTime() time.Time { return tp.VotingEndTime } +func (tp *TextProposal) SetVotingEndTime(votingEndTime time.Time) { + tp.VotingEndTime = votingEndTime +} //----------------------------------------------------------- // ProposalQueue -type ProposalQueue []int64 +type ProposalQueue []uint64 //----------------------------------------------------------- // ProposalKind diff --git a/x/gov/queryable.go b/x/gov/queryable.go index ae7c6d9c55..3e70d43aa0 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -42,7 +42,7 @@ func NewQuerier(keeper Keeper) sdk.Querier { // Params for query 'custom/gov/proposal' type QueryProposalParams struct { - ProposalID int64 + ProposalID uint64 } // nolint: unparam @@ -67,7 +67,7 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper // Params for query 'custom/gov/deposit' type QueryDepositParams struct { - ProposalID int64 + ProposalID uint64 Depositer sdk.AccAddress } @@ -89,7 +89,7 @@ func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper // Params for query 'custom/gov/vote' type QueryVoteParams struct { - ProposalID int64 + ProposalID uint64 Voter sdk.AccAddress } @@ -111,7 +111,7 @@ func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Kee // Params for query 'custom/gov/deposits' type QueryDepositsParams struct { - ProposalID int64 + ProposalID uint64 } // nolint: unparam @@ -139,7 +139,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper // Params for query 'custom/gov/votes' type QueryVotesParams struct { - ProposalID int64 + ProposalID uint64 } // nolint: unparam @@ -168,10 +168,10 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke // Params for query 'custom/gov/proposals' type QueryProposalsParams struct { - Voter sdk.AccAddress - Depositer sdk.AccAddress - ProposalStatus ProposalStatus - NumLatestProposals int64 + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + Limit uint64 } // nolint: unparam @@ -182,7 +182,7 @@ func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keepe return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) } - proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.Limit) bz, err2 := codec.MarshalJSONIndent(keeper.cdc, proposals) if err2 != nil { @@ -193,19 +193,21 @@ func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keepe // Params for query 'custom/gov/tally' type QueryTallyParams struct { - ProposalID int64 + ProposalID uint64 } // nolint: unparam func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { // TODO: Dependant on #1914 - var proposalID int64 - err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID) + var params QueryTallyParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) } + proposalID := params.ProposalID + proposal := keeper.GetProposal(ctx, proposalID) if proposal == nil { return nil, ErrUnknownProposal(DefaultCodespace, proposalID) diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index d5ff1881e4..0eadc7febb 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -50,7 +50,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe if err != nil { return "", nil, err } - action, ok := simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) + action, ok := simulateHandleMsgSubmitProposal(msg, handler, ctx, event) // don't schedule votes if proposal failed if !ok { return action, nil, nil @@ -64,11 +64,11 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe whoVotes := r.Perm(len(accs)) // didntVote := whoVotes[numVotes:] whoVotes = whoVotes[:numVotes] - votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod + votingPeriod := k.GetVotingParams(ctx).VotingPeriod fops := make([]simulation.FutureOperation, numVotes+1) for i := 0; i < numVotes; i++ { whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) - fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, accs[whoVotes[i]], proposalID)} + fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, accs[whoVotes[i]], proposalID)} } // 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant) // TODO: Find a way to check if a validator was slashed other than just checking their balance a block @@ -80,7 +80,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe // SimulateMsgSubmitProposal simulates a msg Submit Proposal // Note: Currently doesn't ensure that the proposal txt is in JSON form -func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation { +func SimulateMsgSubmitProposal(k gov.Keeper) simulation.Operation { handler := gov.NewHandler(k) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { sender := simulation.RandomAcc(r, accs) @@ -88,22 +88,14 @@ func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operati if err != nil { return "", nil, err } - action, _ = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) + action, _ = simulateHandleMsgSubmitProposal(msg, handler, ctx, event) return action, nil, nil } } -func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string, ok bool) { - ctx, write := ctx.CacheContext() - result := handler(ctx, msg) - ok = result.IsOK() - if ok { - // Update pool to keep invariants - pool := sk.GetPool(ctx) - pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom))) - sk.SetPool(ctx, pool) - write() - } +func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string, ok bool) { + ctx, _ = ctx.CacheContext() + handler(ctx, msg) event(fmt.Sprintf("gov/MsgSubmitProposal/%v", ok)) action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", ok, msg.GetSignBytes()) return @@ -125,7 +117,7 @@ func simulationCreateMsgSubmitProposal(r *rand.Rand, sender simulation.Account) } // SimulateMsgDeposit -func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { +func SimulateMsgDeposit(k gov.Keeper) simulation.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { acc := simulation.RandomAcc(r, accs) proposalID, ok := randomProposalID(r, k, ctx) @@ -137,15 +129,8 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } - ctx, write := ctx.CacheContext() + ctx, _ = ctx.CacheContext() result := gov.NewHandler(k)(ctx, msg) - if result.IsOK() { - // Update pool to keep invariants - pool := sk.GetPool(ctx) - pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(deposit.AmountOf(denom))) - sk.SetPool(ctx, pool) - write() - } event(fmt.Sprintf("gov/MsgDeposit/%v", result.IsOK())) action = fmt.Sprintf("TestMsgDeposit: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) return action, nil, nil @@ -154,12 +139,12 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { // SimulateMsgVote // nolint: unparam -func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation { - return operationSimulateMsgVote(k, sk, simulation.Account{}, -1) +func SimulateMsgVote(k gov.Keeper) simulation.Operation { + return operationSimulateMsgVote(k, simulation.Account{}, 0) } // nolint: unparam -func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, acc simulation.Account, proposalID int64) simulation.Operation { +func operationSimulateMsgVote(k gov.Keeper, acc simulation.Account, proposalID uint64) simulation.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { if acc.Equals(simulation.Account{}) { acc = simulation.RandomAcc(r, accs) @@ -200,12 +185,12 @@ func randomDeposit(r *rand.Rand) sdk.Coins { } // Pick a random proposal ID -func randomProposalID(r *rand.Rand, k gov.Keeper, ctx sdk.Context) (proposalID int64, ok bool) { +func randomProposalID(r *rand.Rand, k gov.Keeper, ctx sdk.Context) (proposalID uint64, ok bool) { lastProposalID := k.GetLastProposalID(ctx) if lastProposalID < 1 { return 0, false } - proposalID = int64(r.Intn(int(lastProposalID))) + proposalID = uint64(r.Intn(int(lastProposalID))) return proposalID, true } diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index a222c19a52..5b6068c577 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -59,9 +59,9 @@ func TestGovWithRandomMessages(t *testing.T) { simulation.Simulate( t, mapp.BaseApp, appStateFn, []simulation.WeightedOperation{ - {2, SimulateMsgSubmitProposal(govKeeper, stakeKeeper)}, - {3, SimulateMsgDeposit(govKeeper, stakeKeeper)}, - {20, SimulateMsgVote(govKeeper, stakeKeeper)}, + {2, SimulateMsgSubmitProposal(govKeeper)}, + {3, SimulateMsgDeposit(govKeeper)}, + {20, SimulateMsgVote(govKeeper)}, }, []simulation.RandSetup{ setup, }, []simulation.Invariant{ @@ -75,7 +75,7 @@ func TestGovWithRandomMessages(t *testing.T) { t, mapp.BaseApp, appStateFn, []simulation.WeightedOperation{ {10, SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper)}, - {5, SimulateMsgDeposit(govKeeper, stakeKeeper)}, + {5, SimulateMsgDeposit(govKeeper)}, }, []simulation.RandSetup{ setup, }, []simulation.Invariant{ diff --git a/x/gov/tally.go b/x/gov/tally.go index e839bbe039..d66237762e 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -84,7 +84,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall totalVotingPower = totalVotingPower.Add(votingPower) } - tallyingProcedure := keeper.GetTallyingProcedure(ctx) + tallyParams := keeper.GetTallyParams(ctx) tallyResults = TallyResult{ Yes: results[OptionYes], @@ -98,11 +98,11 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall return false, tallyResults } // If more than 1/3 of voters veto, proposal fails - if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyingProcedure.Veto) { + if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) { return false, tallyResults } // If more than 1/2 of non-abstaining voters vote Yes, proposal passes - if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyingProcedure.Threshold) { + if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyParams.Threshold) { return true, tallyResults } // If more than 1/2 of non-abstaining voters vote No, proposal fails From 2779f4dba35c2cd631a894cf34d05205e349150c Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Wed, 7 Nov 2018 00:03:02 -0800 Subject: [PATCH 54/61] Add general merkle absence proof (also for empty substores) (#2685) --- PENDING.md | 1 + store/iavlstore.go | 17 +++++++-- store/multistoreproof_test.go | 66 +++++++++++++++++++++++++++++++++++ store/rootmultistore.go | 4 +++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index 232d28f631..9796c00881 100644 --- a/PENDING.md +++ b/PENDING.md @@ -52,6 +52,7 @@ IMPROVEMENTS - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - \#2610 [x/stake] Block redelegation to and from the same validator - \#2652 [x/auth] Add benchmark for get and set account + - \#2685 [x/store] Add general merkle absence proof (also for empty substores) * Tendermint diff --git a/store/iavlstore.go b/store/iavlstore.go index bb588f029b..fccde38e27 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -225,8 +225,21 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Log = err.Error() break } - res.Value = value - res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} + if proof == nil { + // Proof == nil implies that the store is empty. + if value != nil { + panic("unexpected value for an empty proof") + } + } + if value != nil { + // value was found + res.Value = value + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} + } else { + // value wasn't found + res.Value = nil + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLAbsenceOp(key, proof).ProofOp()}} + } } else { _, res.Value = tree.GetVersioned(key, res.Height) } diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index 19feef1600..db3b65cad5 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -106,3 +106,69 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) require.NotNil(t, err) } + +func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) + cid := store.Commit() // Commit with empty iavl store. + + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY") + require.Nil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) +} + +func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) + + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() // Commit with empty iavl store. + + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYABSENTKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY") + require.Nil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyAbsence(res.Proof, cid.Hash, "/MYABSENTKEY") + require.NotNil(t, err) + + // Verify (bad) proof. + prt = DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY", []byte("")) + require.NotNil(t, err) +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 6c491bda13..cd2d0135f1 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,6 +295,10 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } + if res.Proof == nil || len(res.Proof.Ops) == 0 { + return sdk.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() + } + commitInfo, errMsg := getCommitInfo(rs.db, res.Height) if errMsg != nil { return sdk.ErrInternal(errMsg.Error()).QueryResult() From 2a3d1f1e87392fbdaa72972aa077c47c46fdebf3 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Wed, 7 Nov 2018 00:14:48 -0800 Subject: [PATCH 55/61] Fix coins.IsLT() impl (#2686) * Fix coins.IsLT() impl * Fix coin.IsLT() impl * Coins.IsLT -> Coins.IsAllLT etc --- PENDING.md | 1 + types/coin.go | 56 ++++++++++++++++++++++++-------- types/coin_test.go | 65 ++++++++++++++++++++++++++++++++++--- x/auth/ante.go | 3 +- x/bank/client/cli/sendtx.go | 2 +- x/bank/keeper.go | 2 +- x/gov/keeper.go | 2 +- 7 files changed, 110 insertions(+), 21 deletions(-) diff --git a/PENDING.md b/PENDING.md index 9796c00881..c399a50998 100644 --- a/PENDING.md +++ b/PENDING.md @@ -69,5 +69,6 @@ BUG FIXES * SDK - \#2625 [x/gov] fix AppendTag function usage error - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator + - \#2674 [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see \#2686) * Tendermint diff --git a/types/coin.go b/types/coin.go index d7484a6699..31f0e98a42 100644 --- a/types/coin.go +++ b/types/coin.go @@ -49,7 +49,7 @@ func (coin Coin) IsGTE(other Coin) bool { // IsLT returns true if they are the same type and the receiver is // a smaller value func (coin Coin) IsLT(other Coin) bool { - return !coin.IsGTE(other) + return coin.SameDenomAs(other) && coin.Amount.LT(other.Amount) } // IsEqual returns true if the two sets of Coins have the same value @@ -142,7 +142,11 @@ func (coins Coins) Plus(coinsB Coins) Coins { coinA, coinB := coins[indexA], coinsB[indexB] switch strings.Compare(coinA.Denom, coinB.Denom) { case -1: - sum = append(sum, coinA) + if coinA.IsZero() { + // ignore 0 sum coin type + } else { + sum = append(sum, coinA) + } indexA++ case 0: if coinA.Amount.Add(coinB.Amount).IsZero() { @@ -153,7 +157,11 @@ func (coins Coins) Plus(coinsB Coins) Coins { indexA++ indexB++ case 1: - sum = append(sum, coinB) + if coinB.IsZero() { + // ignore 0 sum coin type + } else { + sum = append(sum, coinB) + } indexB++ } } @@ -176,10 +184,19 @@ func (coins Coins) Minus(coinsB Coins) Coins { return coins.Plus(coinsB.Negative()) } -// IsGTE returns True iff coins is NonNegative(), and for every -// currency in coinsB, the currency is present at an equal or greater -// amount in coinsB -func (coins Coins) IsGTE(coinsB Coins) bool { +// IsAllGT returns True iff for every denom in coins, the denom is present at a +// greater amount in coinsB. +func (coins Coins) IsAllGT(coinsB Coins) bool { + diff := coins.Minus(coinsB) + if len(diff) == 0 { + return false + } + return diff.IsPositive() +} + +// IsAllGTE returns True iff for every denom in coins, the denom is present at an +// equal or greater amount in coinsB. +func (coins Coins) IsAllGTE(coinsB Coins) bool { diff := coins.Minus(coinsB) if len(diff) == 0 { return true @@ -187,14 +204,27 @@ func (coins Coins) IsGTE(coinsB Coins) bool { return diff.IsNotNegative() } -// IsLT returns True iff every currency in coins, the currency is -// present at a smaller amount in coins -func (coins Coins) IsLT(coinsB Coins) bool { - return !coins.IsGTE(coinsB) +// IsAllLT returns True iff for every denom in coins, the denom is present at +// a smaller amount in coinsB. +func (coins Coins) IsAllLT(coinsB Coins) bool { + diff := coinsB.Minus(coins) + if len(diff) == 0 { + return false + } + return diff.IsPositive() } -// IsZero returns true if there are no coins -// or all coins are zero. +// IsAllLTE returns True iff for every denom in coins, the denom is present at +// a smaller or equal amount in coinsB. +func (coins Coins) IsAllLTE(coinsB Coins) bool { + diff := coinsB.Minus(coins) + if len(diff) == 0 { + return true + } + return diff.IsNotNegative() +} + +// IsZero returns true if there are no coins or all coins are zero. func (coins Coins) IsZero() bool { for _, coin := range coins { if !coin.IsZero() { diff --git a/types/coin_test.go b/types/coin_test.go index bc04412791..ac9fcb391b 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -86,7 +86,10 @@ func TestIsLTCoin(t *testing.T) { {NewInt64Coin("A", 1), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", 2), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", -1), NewInt64Coin("A", 5), true}, - {NewInt64Coin("a", 0), NewInt64Coin("b", 1), true}, + {NewInt64Coin("a", 0), NewInt64Coin("b", 1), false}, + {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, + {NewInt64Coin("a", 1), NewInt64Coin("a", 1), false}, + {NewInt64Coin("a", 1), NewInt64Coin("a", 2), true}, } for tcIndex, tc := range cases { @@ -245,9 +248,9 @@ func TestCoins(t *testing.T) { assert.True(t, good.IsValid(), "Coins are valid") assert.True(t, good.IsPositive(), "Expected coins to be positive: %v", good) assert.False(t, null.IsPositive(), "Expected coins to not be positive: %v", null) - assert.True(t, good.IsGTE(empty), "Expected %v to be >= %v", good, empty) - assert.False(t, good.IsLT(empty), "Expected %v to be < %v", good, empty) - assert.True(t, empty.IsLT(good), "Expected %v to be < %v", empty, good) + assert.True(t, good.IsAllGTE(empty), "Expected %v to be >= %v", good, empty) + assert.False(t, good.IsAllLT(empty), "Expected %v to be < %v", good, empty) + assert.True(t, empty.IsAllLT(good), "Expected %v to be < %v", empty, good) assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg) assert.Zero(t, len(sum), "Expected 0 coins") assert.False(t, badSort1.IsValid(), "Coins are not sorted") @@ -257,6 +260,60 @@ func TestCoins(t *testing.T) { } +func TestCoinsGT(t *testing.T) { + one := NewInt(1) + two := NewInt(2) + + assert.False(t, Coins{}.IsAllGT(Coins{})) + assert.True(t, Coins{{"A", one}}.IsAllGT(Coins{})) + assert.False(t, Coins{{"A", one}}.IsAllGT(Coins{{"A", one}})) + assert.False(t, Coins{{"A", one}}.IsAllGT(Coins{{"B", one}})) + assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllGT(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllGT(Coins{{"B", two}})) +} + +func TestCoinsGTE(t *testing.T) { + one := NewInt(1) + two := NewInt(2) + + assert.True(t, Coins{}.IsAllGTE(Coins{})) + assert.True(t, Coins{{"A", one}}.IsAllGTE(Coins{})) + assert.True(t, Coins{{"A", one}}.IsAllGTE(Coins{{"A", one}})) + assert.False(t, Coins{{"A", one}}.IsAllGTE(Coins{{"B", one}})) + assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllGTE(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllGTE(Coins{{"B", two}})) +} + +func TestCoinsLT(t *testing.T) { + one := NewInt(1) + two := NewInt(2) + + assert.False(t, Coins{}.IsAllLT(Coins{})) + assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{})) + assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{{"A", one}})) + assert.False(t, Coins{{"A", one}}.IsAllLT(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"B", two}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"A", one}, {"B", one}})) + assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLT(Coins{{"A", one}, {"B", two}})) + assert.True(t, Coins{}.IsAllLT(Coins{{"A", one}})) +} + +func TestCoinsLTE(t *testing.T) { + one := NewInt(1) + two := NewInt(2) + + assert.True(t, Coins{}.IsAllLTE(Coins{})) + assert.False(t, Coins{{"A", one}}.IsAllLTE(Coins{})) + assert.True(t, Coins{{"A", one}}.IsAllLTE(Coins{{"A", one}})) + assert.False(t, Coins{{"A", one}}.IsAllLTE(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"B", one}})) + assert.False(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"B", two}})) + assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"A", one}, {"B", one}})) + assert.True(t, Coins{{"A", one}, {"B", one}}.IsAllLTE(Coins{{"A", one}, {"B", two}})) + assert.True(t, Coins{}.IsAllLTE(Coins{{"A", one}})) +} + func TestPlusCoins(t *testing.T) { one := NewInt(1) zero := NewInt(0) diff --git a/x/auth/ante.go b/x/auth/ante.go index 9d5bc50597..e88a20a434 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -282,7 +282,8 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { // TODO: Make the gasPrice not a constant, and account for tx size. requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas) - if !ctx.MinimumFees().IsZero() && stdTx.Fee.Amount.IsLT(requiredFees) { + // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). + if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) { // validators reject any tx from the mempool with less than the minimum fee per gas * gas factor return sdk.ErrInsufficientFee(fmt.Sprintf( "insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees)).Result() diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index c72ed6fcb9..bae2179766 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -59,7 +59,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { } // ensure account has enough coins - if !account.GetCoins().IsGTE(coins) { + if !account.GetCoins().IsAllGTE(coins) { return errors.Errorf("Address %s doesn't have enough coins to pay for this transaction.", from) } diff --git a/x/bank/keeper.go b/x/bank/keeper.go index ac7a484d5e..67c05d3206 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -190,7 +190,7 @@ func setCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt s // HasCoins returns whether or not an account has at least amt coins. func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) bool { ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins") - return getCoins(ctx, am, addr).IsGTE(amt) + return getCoins(ctx, am, addr).IsAllGTE(amt) } // SubtractCoins subtracts amt from the coins at the addr. diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 48d9000d81..862db82744 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -385,7 +385,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositerAdd // Check if deposit tipped proposal into voting period // Active voting period if so activatedVotingPeriod := false - if proposal.GetStatus() == StatusDepositPeriod && proposal.GetTotalDeposit().IsGTE(keeper.GetDepositParams(ctx).MinDeposit) { + if proposal.GetStatus() == StatusDepositPeriod && proposal.GetTotalDeposit().IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) { keeper.activateVotingPeriod(ctx, proposal) activatedVotingPeriod = true } From 4f46a4c4d5336ee429521b3dde9f8c8c4ab123df Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 7 Nov 2018 03:19:06 -0500 Subject: [PATCH 56/61] Update testnet to use canonical genesis time (#2692) * Update testnet to use canonical genesis time * Fix linting in genesis test --- Gopkg.lock | 1 + PENDING.md | 3 +- cmd/gaia/app/genesis_test.go | 9 +- cmd/gaia/init/collect.go | 39 ++++---- cmd/gaia/init/init.go | 55 ----------- cmd/gaia/init/testnet.go | 177 +++++++++++++++++++++++++---------- cmd/gaia/init/utils.go | 112 ++++++++++++++++++++++ 7 files changed, 266 insertions(+), 130 deletions(-) create mode 100644 cmd/gaia/init/utils.go diff --git a/Gopkg.lock b/Gopkg.lock index 0e9681ed39..8dd4be7083 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -689,6 +689,7 @@ "github.com/tendermint/tendermint/rpc/lib/client", "github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/types", + "github.com/tendermint/tendermint/types/time", "github.com/tendermint/tendermint/version", "github.com/zondax/ledger-goclient", "golang.org/x/crypto/bcrypt", diff --git a/PENDING.md b/PENDING.md index c399a50998..b3885942b9 100644 --- a/PENDING.md +++ b/PENDING.md @@ -64,7 +64,8 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2670 [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK - \#2625 [x/gov] fix AppendTag function usage error diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 6c331856c3..579ad93a85 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -2,9 +2,10 @@ package app import ( "encoding/json" + "testing" + "github.com/tendermint/tendermint/crypto/secp256k1" tmtypes "github.com/tendermint/tendermint/types" - "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -91,8 +92,10 @@ func TestGaiaAppGenState(t *testing.T) { func makeMsg(name string, pk crypto.PubKey) auth.StdTx { desc := stake.NewDescription(name, "", "", "") comm := stakeTypes.CommissionMsg{} - msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(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/init/collect.go b/cmd/gaia/init/collect.go index 34b65e560b..97d9743a06 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -2,7 +2,6 @@ package init import ( "encoding/json" - "io/ioutil" "path/filepath" "github.com/cosmos/cosmos-sdk/client" @@ -12,7 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/go-amino" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/cli" @@ -35,12 +33,13 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) - name := viper.GetString(client.FlagName) + nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) if err != nil { return err } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) if err != nil { return err @@ -59,12 +58,14 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { NodeID: nodeID, ValPubKey: valPubKey, } + appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) if err != nil { return err } toPrint.AppMessage = appMessage + // print out some key information return displayInfo(cdc, toPrint) }, @@ -74,23 +75,29 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return cmd } -func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig, - genDoc types.GenesisDoc) (appState json.RawMessage, err error) { +func genAppStateFromConfig( + cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc, +) (appState json.RawMessage, err error) { genFile := config.GenesisFile() - // process genesis transactions, else create default genesis.json - var appGenTxs []auth.StdTx - var persistentPeers string - var genTxs []json.RawMessage - var jsonRawTx json.RawMessage + var ( + appGenTxs []auth.StdTx + persistentPeers string + genTxs []json.RawMessage + jsonRawTx json.RawMessage + ) + // process genesis transactions, else create default genesis.json appGenTxs, persistentPeers, err = app.CollectStdTxs( - cdc, config.Moniker, initCfg.GenTxsDir, genDoc) + cdc, config.Moniker, initCfg.GenTxsDir, genDoc, + ) if err != nil { return } + genTxs = make([]json.RawMessage, len(appGenTxs)) config.P2P.PersistentPeers = persistentPeers + for i, stdTx := range appGenTxs { jsonRawTx, err = cdc.MarshalJSON(stdTx) if err != nil { @@ -100,6 +107,7 @@ func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initCon } cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs) if err != nil { return @@ -108,12 +116,3 @@ func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initCon err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState) return } - -func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { - genContents, err := ioutil.ReadFile(genFile) - if err != nil { - return - } - err = cdc.UnmarshalJSON(genContents, &genDoc) - return -} diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 0290ae4fb2..f2a815a5c3 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -3,7 +3,6 @@ package init import ( "encoding/json" "fmt" - "github.com/tendermint/tendermint/privval" "os" "path/filepath" @@ -14,11 +13,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/types" ) const ( @@ -97,54 +93,3 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob cmd.Flags().String(flagMoniker, "", "set the validator's moniker") return cmd } - -// InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) { - nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) - if err != nil { - return - } - nodeID = string(nodeKey.ID()) - valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile()) - return -} - -// WriteGenesisFile creates and writes the genesis configuration to disk. An -// error is returned if building or writing the configuration to file fails. -// nolint: unparam -func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error { - genDoc := types.GenesisDoc{ - ChainID: chainID, - Validators: validators, - AppState: appState, - } - - if err := genDoc.ValidateAndComplete(); err != nil { - return err - } - - return genDoc.SaveAs(genesisFile) -} - -// read of create the private key file for this config -func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey { - // private validator - var privValidator *privval.FilePV - if common.FileExists(privValFile) { - privValidator = privval.LoadFilePV(privValFile) - } else { - privValidator = privval.GenFilePV(privValFile) - privValidator.Save() - } - return privValidator.GetPubKey() -} - -func initializeEmptyGenesis(cdc *codec.Codec, genFile string, chainID string, - overwrite bool) (appState json.RawMessage, err error) { - if !overwrite && common.FileExists(genFile) { - err = fmt.Errorf("genesis.json file already exists: %v", genFile) - return - } - - return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState()) -} diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 5f729b0a4b..42d8332d27 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -3,6 +3,10 @@ package init import ( "encoding/json" "fmt" + "net" + "os" + "path/filepath" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" @@ -10,10 +14,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/tendermint/tendermint/types" - "net" - "os" - "path/filepath" "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" @@ -21,16 +21,17 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" ) var ( - nodeDirPrefix = "node-dir-prefix" - nValidators = "v" - outputDir = "output-dir" - nodeDaemonHome = "node-daemon-home" - nodeCliHome = "node-cli-home" - - startingIPAddress = "starting-ip-address" + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagNodeCliHome = "node-cli-home" + flagStartingIPAddress = "starting-ip-address" ) const nodeDirPerm = 0755 @@ -48,50 +49,59 @@ necessary files (private validator, genesis, config, etc.). Note, strict routability for addresses is turned off in the config file. Example: - - gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2 + gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config - return testnetWithConfig(config, cdc) + return initTestnet(config, cdc) }, } - cmd.Flags().Int(nValidators, 4, - "Number of validators to initialize the testnet with") - cmd.Flags().StringP(outputDir, "o", "./mytestnet", - "Directory to store initialization data for the testnet") - cmd.Flags().String(nodeDirPrefix, "node", - "Prefix the directory name for each node with (node results in node0, node1, ...)") - cmd.Flags().String(nodeDaemonHome, "gaiad", - "Home directory of the node's daemon configuration") - cmd.Flags().String(nodeCliHome, "gaiacli", - "Home directory of the node's cli configuration") - cmd.Flags().String(startingIPAddress, "192.168.0.1", + cmd.Flags().Int(flagNumValidators, 4, + "Number of validators to initialize the testnet with", + ) + cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", + "Directory to store initialization data for the testnet", + ) + cmd.Flags().String(flagNodeDirPrefix, "node", + "Prefix the directory name for each node with (node results in node0, node1, ...)", + ) + cmd.Flags().String(flagNodeDaemonHome, "gaiad", + "Home directory of the node's daemon configuration", + ) + cmd.Flags().String(flagNodeCliHome, "gaiacli", + "Home directory of the node's cli configuration", + ) + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + return cmd } -func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { - outDir := viper.GetString(outputDir) - numValidators := viper.GetInt(nValidators) +func initTestnet(config *cfg.Config, cdc *codec.Codec) error { + outDir := viper.GetString(flagOutputDir) + numValidators := viper.GetInt(flagNumValidators) - // Generate genesis.json and config.toml chainID := "chain-" + cmn.RandStr(6) + monikers := make([]string, numValidators) nodeIDs := make([]string, numValidators) valPubKeys := make([]crypto.PubKey, numValidators) - // Generate private key, node ID, initial transaction - var accs []app.GenesisAccount - var genFiles []string + var ( + accs []app.GenesisAccount + genFiles []string + ) + + // generate private keys, node IDs, and initial transactions for i := 0; i < numValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) - nodeDaemonHomeName := viper.GetString(nodeDaemonHome) - nodeCliHomeName := viper.GetString(nodeCliHome) + nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i) + nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome) + nodeCliHomeName := viper.GetString(flagNodeCliHome) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName) gentxsDir := filepath.Join(outDir, "gentxs") + config.SetRoot(nodeDir) err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) @@ -108,24 +118,27 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { monikers = append(monikers, nodeDirName) config.Moniker = nodeDirName - ip, err := getIP(i) + + ip, err := getIP(i, viper.GetString(flagStartingIPAddress)) if err != nil { _ = os.RemoveAll(outDir) return err } + nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config) if err != nil { _ = os.RemoveAll(outDir) return err } - memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) - // write genesis + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) genFiles = append(genFiles, config.GenesisFile()) buf := client.BufferStdin() prompt := fmt.Sprintf( - "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) + "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass, + ) + keyPass, err := client.GetPassword(prompt, buf) if err != nil && keyPass != "" { // An error was returned that either failed to read the password from @@ -133,6 +146,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { // length requirements. return err } + if keyPass == "" { keyPass = app.DefaultKeyPass } @@ -142,11 +156,14 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { _ = os.RemoveAll(outDir) return err } + info := map[string]string{"secret": secret} + cliPrint, err := json.Marshal(info) if err != nil { return err } + // save private key seed words err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint) if err != nil { @@ -170,6 +187,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { ) tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo) txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo) + signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false) if err != nil { _ = os.RemoveAll(outDir) @@ -182,7 +200,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { return err } - // Gather gentxs folder + // gather gentxs folder err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes) if err != nil { _ = os.RemoveAll(outDir) @@ -190,37 +208,70 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { } } - // Generate empty genesis.json + if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil { + return err + } + + err := collectGenFiles( + cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators, + outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome), + ) + if err != nil { + return err + } + + fmt.Printf("Successfully initialized %d node directories\n", numValidators) + return nil +} + +func initGenFiles( + cdc *codec.Codec, chainID string, accs []app.GenesisAccount, + genFiles []string, numValidators int, +) error { + appGenState := app.NewDefaultGenesisState() appGenState.Accounts = accs + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) if err != nil { return err } + genDoc := types.GenesisDoc{ ChainID: chainID, AppState: appGenStateJSON, Validators: nil, } - // Save all genesis.json files + // generate empty genesis files for each validator and save for i := 0; i < numValidators; i++ { if err := genDoc.SaveAs(genFiles[i]); err != nil { return err } } + return nil +} + +func collectGenFiles( + cdc *codec.Codec, config *cfg.Config, chainID string, + monikers, nodeIDs []string, valPubKeys []crypto.PubKey, + numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string, +) error { + + var appState json.RawMessage + genTime := tmtime.Now() + for i := 0; i < numValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) - nodeDaemonHomeName := viper.GetString(nodeDaemonHome) + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) gentxsDir := filepath.Join(outDir, "gentxs") moniker := monikers[i] config.Moniker = nodeDirName + config.SetRoot(nodeDir) nodeID, valPubKey := nodeIDs[i], valPubKeys[i] - // Run `init` and generate genesis.json and config.toml initCfg := initConfig{ ChainID: chainID, GenTxsDir: gentxsDir, @@ -228,46 +279,69 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { NodeID: nodeID, ValPubKey: valPubKey, } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) if err != nil { return err } - if _, err := genAppStateFromConfig(cdc, config, initCfg, genDoc); err != nil { + + nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := config.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + err = WriteGenesisFileWithTime(genFile, chainID, nil, appState, genTime) + if err != nil { return err } } - fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators)) return nil } -func getIP(i int) (ip string, err error) { - ip = viper.GetString(startingIPAddress) - if len(ip) == 0 { +func getIP(i int, startingIPAddr string) (string, error) { + var ( + ip string + err error + ) + + if len(startingIPAddr) == 0 { ip, err = server.ExternalIP() if err != nil { return "", err } } else { - ip, err = calculateIP(ip, i) + ip, err = calculateIP(startingIPAddr, i) if err != nil { return "", err } } + return ip, nil } func writeFile(name string, dir string, contents []byte) error { writePath := filepath.Join(dir) file := filepath.Join(writePath, name) + err := cmn.EnsureDir(writePath, 0700) if err != nil { return err } + err = cmn.WriteFile(file, contents, 0600) if err != nil { return err } + return nil } @@ -280,5 +354,6 @@ func calculateIP(ip string, i int) (string, error) { for j := 0; j < i; j++ { ipv4[3]++ } + return ipv4.String(), nil } diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go new file mode 100644 index 0000000000..1738433705 --- /dev/null +++ b/cmd/gaia/init/utils.go @@ -0,0 +1,112 @@ +package init + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "time" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + amino "github.com/tendermint/go-amino" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/types" +) + +// WriteGenesisFile creates and writes the genesis configuration to disk. An +// error is returned if building or writing the configuration to file fails. +func WriteGenesisFile( + genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, +) error { + + genDoc := types.GenesisDoc{ + ChainID: chainID, + Validators: validators, + AppState: appState, + } + + if err := genDoc.ValidateAndComplete(); err != nil { + return err + } + + return genDoc.SaveAs(genFile) +} + +// WriteGenesisFileWithTime creates and writes the genesis configuration to disk. +// An error is returned if building or writing the configuration to file fails. +func WriteGenesisFileWithTime( + genFile, chainID string, validators []types.GenesisValidator, + appState json.RawMessage, genTime time.Time, +) error { + + genDoc := types.GenesisDoc{ + GenesisTime: genTime, + ChainID: chainID, + Validators: validators, + AppState: appState, + } + + if err := genDoc.ValidateAndComplete(); err != nil { + return err + } + + return genDoc.SaveAs(genFile) +} + +// read of create the private key file for this config +func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey { + var privValidator *privval.FilePV + + if common.FileExists(privValFile) { + privValidator = privval.LoadFilePV(privValFile) + } else { + privValidator = privval.GenFilePV(privValFile) + privValidator.Save() + } + + return privValidator.GetPubKey() +} + +// InitializeNodeValidatorFiles creates private validator and p2p configuration files. +func InitializeNodeValidatorFiles( + config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error, +) { + + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return nodeID, valPubKey, err + } + + nodeID = string(nodeKey.ID()) + valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile()) + + return nodeID, valPubKey, nil +} + +func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { + genContents, err := ioutil.ReadFile(genFile) + if err != nil { + return genDoc, err + } + + if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil { + return genDoc, err + } + + return genDoc, err +} + +func initializeEmptyGenesis( + cdc *codec.Codec, genFile, chainID string, overwrite bool, +) (appState json.RawMessage, err error) { + + if !overwrite && common.FileExists(genFile) { + return nil, fmt.Errorf("genesis.json file already exists: %v", genFile) + } + + return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState()) +} From c7b3efdd02be2132f19c0ad4afc58b002edc73fe Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Wed, 7 Nov 2018 00:29:27 -0800 Subject: [PATCH 57/61] Do not allow nil values to be set in CacheKVStore (#2708) * Do not allow nil values to be set in CacheKVStore --- PENDING.md | 3 ++- store/cachekvstore.go | 7 +++++++ types/store.go | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index b3885942b9..7a4f32a62b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -52,7 +52,8 @@ IMPROVEMENTS - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - \#2610 [x/stake] Block redelegation to and from the same validator - \#2652 [x/auth] Add benchmark for get and set account - - \#2685 [x/store] Add general merkle absence proof (also for empty substores) + - \#2685 [store] Add general merkle absence proof (also for empty substores) + - \#2708 [store] Disallow setting nil values * Tendermint diff --git a/store/cachekvstore.go b/store/cachekvstore.go index 9eb4ae9324..4a2940f4df 100644 --- a/store/cachekvstore.go +++ b/store/cachekvstore.go @@ -61,6 +61,7 @@ func (ci *cacheKVStore) Set(key []byte, value []byte) { ci.mtx.Lock() defer ci.mtx.Unlock() ci.assertValidKey(key) + ci.assertValidValue(value) ci.setCacheValue(key, value, false, true) } @@ -196,6 +197,12 @@ func (ci *cacheKVStore) assertValidKey(key []byte) { } } +func (ci *cacheKVStore) assertValidValue(value []byte) { + if value == nil { + panic("value is nil") + } +} + // Only entrypoint to mutate ci.cache. func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) { ci.cache[string(key)] = cValue{ diff --git a/types/store.go b/types/store.go index 7bab93b97e..a90dc223dd 100644 --- a/types/store.go +++ b/types/store.go @@ -127,7 +127,7 @@ type KVStore interface { // Has checks if a key exists. Panics on nil key. Has(key []byte) bool - // Set sets the key. Panics on nil key. + // Set sets the key. Panics on nil key or value. Set(key, value []byte) // Delete deletes the key. Panics on nil key. From 8f690b5b6c6e58bbc94809fc2bb854e075aded6f Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 7 Nov 2018 07:55:59 -0500 Subject: [PATCH 58/61] Merge PR #2714: Add commission data to MsgCreateValidator signature bytes --- PENDING.md | 1 + x/stake/types/msg.go | 1 + 2 files changed, 2 insertions(+) diff --git a/PENDING.md b/PENDING.md index 7a4f32a62b..8b2044ea29 100644 --- a/PENDING.md +++ b/PENDING.md @@ -72,5 +72,6 @@ BUG FIXES - \#2625 [x/gov] fix AppendTag function usage error - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator - \#2674 [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see \#2686) + - \#2711 [x/stake] Add commission data to `MsgCreateValidator` signature bytes. * Tendermint diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index da7d684de7..63d0afb5fb 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -68,6 +68,7 @@ func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress { func (msg MsgCreateValidator) GetSignBytes() []byte { b, err := MsgCdc.MarshalJSON(struct { Description + Commission CommissionMsg DelegatorAddr sdk.AccAddress `json:"delegator_address"` ValidatorAddr sdk.ValAddress `json:"validator_address"` PubKey string `json:"pubkey"` From 94f45311a04f2f5e51046d23cf4885c3045262f9 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 01:28:28 +0100 Subject: [PATCH 59/61] Fix state export/import, add to CI (#2690) * Update slashing import/export * More slashing.WriteGenesis * Add test import/export to CI * Store equality comparison. * Fix validator bond intra-tx counter * Set timeslices for unbonding validators * WriteGenesis => ExportGenesis * Delete validators from unbonding queue when re-bonded * Hook for validator deletion, fix staking genesis tests --- .circleci/config.yml | 21 +++++ Makefile | 12 ++- PENDING.md | 5 +- cmd/gaia/app/app.go | 32 +++++--- cmd/gaia/app/genesis.go | 30 ++++--- cmd/gaia/app/sim_test.go | 98 +++++++++++++++++++++++ cmd/gaia/init/collect.go | 2 +- cmd/gaia/init/init.go | 2 +- cmd/gaia/init/testnet.go | 2 +- cmd/gaia/init/utils.go | 8 +- docs/spec/staking/hooks.md | 2 +- examples/basecoin/cmd/basecoind/main.go | 2 +- examples/democoin/app/app.go | 4 +- examples/democoin/cmd/democoind/main.go | 2 +- examples/democoin/x/cool/keeper.go | 4 +- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/keeper.go | 4 +- examples/democoin/x/pow/keeper_test.go | 2 +- types/stake.go | 6 +- types/store.go | 38 +++++++++ x/auth/genesis.go | 33 ++++++++ x/auth/{mapper.go => keeper.go} | 0 x/auth/{mapper_test.go => keeper_test.go} | 0 x/distribution/genesis.go | 8 +- x/distribution/keeper/genesis.go | 4 +- x/distribution/keeper/hooks.go | 2 +- x/distribution/keeper/key.go | 9 +++ x/distribution/types/genesis.go | 4 +- x/gov/genesis.go | 59 ++++++++++++-- x/gov/handler.go | 2 +- x/mint/genesis.go | 4 +- x/slashing/genesis.go | 75 ++++++++++++++++- x/slashing/hooks.go | 25 +++++- x/slashing/keeper.go | 16 ---- x/slashing/keeper_test.go | 21 ++--- x/slashing/keys.go | 9 +++ x/slashing/signing_info.go | 33 ++++++++ x/slashing/slashing_period.go | 15 ++++ x/slashing/test_common.go | 2 +- x/slashing/tick_test.go | 3 +- x/stake/genesis.go | 80 +++++++++++++----- x/stake/genesis_test.go | 23 +++--- x/stake/keeper/delegation.go | 15 ++++ x/stake/keeper/hooks.go | 4 +- x/stake/keeper/val_state_change.go | 9 ++- x/stake/keeper/validator.go | 33 +++++++- x/stake/stake.go | 3 + x/stake/types/genesis.go | 16 +++- 48 files changed, 640 insertions(+), 145 deletions(-) create mode 100644 x/auth/genesis.go rename x/auth/{mapper.go => keeper.go} (100%) rename x/auth/{mapper_test.go => keeper_test.go} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 879df07f02..708b5887d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -137,6 +137,24 @@ jobs: export PATH="$GOBIN:$PATH" make test_sim_gaia_fast + test_sim_gaia_import_export: + <<: *defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - run: + name: dependencies + command: | + export PATH="$GOBIN:$PATH" + make get_vendor_deps + - run: + name: Test Gaia import/export simulation + command: | + export PATH="$GOBIN:$PATH" + make test_sim_gaia_import_export + test_sim_gaia_multi_seed: <<: *defaults parallelism: 1 @@ -259,6 +277,9 @@ workflows: - test_sim_gaia_fast: requires: - setup_dependencies + - test_sim_gaia_import_export: + requires: + - setup_dependencies - test_sim_gaia_multi_seed: requires: - setup_dependencies diff --git a/Makefile b/Makefile index af4ea71c98..aaa2fbcf3e 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,15 @@ test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=10 -v -timeout 24h +test_sim_gaia_import_export: + @echo "Running Gaia import/export simulation. This may take several minutes..." + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=11 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=12 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=13 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=414 -v -timeout 24h + @go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=50 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=4142 -v -timeout 24h + test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" @bash scripts/multisim.sh 25 @@ -250,4 +259,5 @@ localnet-stop: check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ -format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_multi_seed update_tools update_dev_tools +format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ +test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools diff --git a/PENDING.md b/PENDING.md index 8b2044ea29..1d5752fc2d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -28,7 +28,7 @@ FEATURES * Gaia * SDK - * (#1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. + - \#1336 Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint @@ -65,7 +65,8 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - \#2670 [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2648 [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 1927d235d1..918bfc67d1 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -210,9 +210,6 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R tags := gov.EndBlocker(ctx, app.govKeeper) validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) - // Add these new validators to the addr -> pubkey map. - app.slashingKeeper.AddValidators(ctx, validatorUpdates) - return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, Tags: tags, @@ -231,6 +228,10 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // return sdk.ErrGenesisParse("").TraceCause(err, "") } + // sort by account number to maintain consistency + sort.Slice(genesisState.Accounts, func(i, j int) bool { + return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber + }) // load the accounts for _, gacc := range genesisState.Accounts { acc := gacc.ToAccount() @@ -244,7 +245,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci panic(err) // TODO find a way to do this w/o panics } - // load the address to pubkey map + // initialize module-specific stores + auth.InitGenesis(ctx, app.feeCollectionKeeper, genesisState.AuthData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) @@ -270,7 +272,6 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } - app.slashingKeeper.AddValidators(ctx, validators) // sanity check if len(req.Validators) > 0 { @@ -306,11 +307,12 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val app.accountKeeper.IterateAccounts(ctx, appendAccount) genState := NewGenesisState( accounts, - stake.WriteGenesis(ctx, app.stakeKeeper), - mint.WriteGenesis(ctx, app.mintKeeper), - distr.WriteGenesis(ctx, app.distrKeeper), - gov.WriteGenesis(ctx, app.govKeeper), - slashing.GenesisState{}, // TODO create write methods + auth.ExportGenesis(ctx, app.feeCollectionKeeper), + stake.ExportGenesis(ctx, app.stakeKeeper), + mint.ExportGenesis(ctx, app.mintKeeper), + distr.ExportGenesis(ctx, app.distrKeeper), + gov.ExportGenesis(ctx, app.govKeeper), + slashing.ExportGenesis(ctx, app.slashingKeeper), ) appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { @@ -337,12 +339,15 @@ var _ sdk.StakingHooks = Hooks{} // nolint func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorCreated(ctx, valAddr) + h.sh.OnValidatorCreated(ctx, valAddr) } func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorModified(ctx, valAddr) + h.sh.OnValidatorModified(ctx, valAddr) } -func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { - h.dh.OnValidatorRemoved(ctx, valAddr) +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + h.dh.OnValidatorRemoved(ctx, consAddr, valAddr) + h.sh.OnValidatorRemoved(ctx, consAddr, valAddr) } func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorBonded(ctx, consAddr, valAddr) @@ -358,10 +363,13 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationCreated(ctx, delAddr, valAddr) + h.sh.OnDelegationCreated(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr) + h.sh.OnDelegationSharesModified(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationRemoved(ctx, delAddr, valAddr) + h.sh.OnDelegationRemoved(ctx, delAddr, valAddr) } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index cfbbe5cc2e..38256e4150 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -32,6 +32,7 @@ var ( // State to Unmarshal type GenesisState struct { Accounts []GenesisAccount `json:"accounts"` + AuthData auth.GenesisState `json:"auth"` StakeData stake.GenesisState `json:"stake"` MintData mint.GenesisState `json:"mint"` DistrData distr.GenesisState `json:"distr"` @@ -40,11 +41,12 @@ type GenesisState struct { GenTxs []json.RawMessage `json:"gentxs"` } -func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState, +func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState, distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { return GenesisState{ Accounts: accounts, + AuthData: authData, StakeData: stakeData, MintData: mintData, DistrData: distrData, @@ -53,31 +55,39 @@ func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mi } } -// GenesisAccount doesn't need pubkey or sequence +// nolint type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Sequence int64 `json:"sequence_number"` + AccountNumber int64 `json:"account_number"` } func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { return GenesisAccount{ - Address: acc.Address, - Coins: acc.Coins, + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, } } func NewGenesisAccountI(acc auth.Account) GenesisAccount { return GenesisAccount{ - Address: acc.GetAddress(), - Coins: acc.GetCoins(), + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + AccountNumber: acc.GetAccountNumber(), + Sequence: acc.GetSequence(), } } // convert GenesisAccount to auth.BaseAccount func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { return &auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), + Address: ga.Address, + Coins: ga.Coins.Sort(), + AccountNumber: ga.AccountNumber, + Sequence: ga.Sequence, } } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 431c5bc415..60befcc385 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -266,6 +267,103 @@ func TestFullGaiaSimulation(t *testing.T) { require.Nil(t, err) } +func TestGaiaImportExport(t *testing.T) { + if !enabled { + t.Skip("Skipping Gaia import/export simulation") + } + + // Setup Gaia application + var logger log.Logger + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") + db, _ = dbm.NewGoLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() + app := NewGaiaApp(logger, db, nil) + require.Equal(t, "GaiaApp", app.Name()) + + // Run randomized simulation + err := simulation.SimulateFromSeed( + t, app.BaseApp, appStateFn, seed, + testAndRunTxs(app), + []simulation.RandSetup{}, + invariants(app), + numBlocks, + blockSize, + commit, + ) + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("GoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } + require.Nil(t, err) + + fmt.Printf("Exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators() + if err != nil { + panic(err) + } + + fmt.Printf("Importing genesis...\n") + + newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2") + newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir) + defer func() { + newDB.Close() + os.RemoveAll(newDir) + }() + newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil) + require.Equal(t, "GaiaApp", newApp.Name()) + request := abci.RequestInitChain{ + AppStateBytes: appState, + } + newApp.InitChain(request) + newApp.Commit() + + fmt.Printf("Comparing stores...\n") + ctxA := app.NewContext(true, abci.Header{}) + ctxB := newApp.NewContext(true, abci.Header{}) + type StoreKeysPrefixes struct { + A sdk.StoreKey + B sdk.StoreKey + Prefixes [][]byte + } + storeKeysPrefixes := []StoreKeysPrefixes{ + {app.keyMain, newApp.keyMain, [][]byte{}}, + {app.keyAccount, newApp.keyAccount, [][]byte{}}, + {app.keyStake, newApp.keyStake, [][]byte{stake.UnbondingQueueKey, stake.RedelegationQueueKey, stake.ValidatorQueueKey}}, // ordering may change but it doesn't matter + {app.keySlashing, newApp.keySlashing, [][]byte{}}, + {app.keyMint, newApp.keyMint, [][]byte{}}, + {app.keyDistr, newApp.keyDistr, [][]byte{}}, + {app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}}, + {app.keyParams, newApp.keyParams, [][]byte{}}, + {app.keyGov, newApp.keyGov, [][]byte{}}, + } + for _, storeKeysPrefix := range storeKeysPrefixes { + storeKeyA := storeKeysPrefix.A + storeKeyB := storeKeysPrefix.B + prefixes := storeKeysPrefix.Prefixes + storeA := ctxA.KVStore(storeKeyA) + storeB := ctxB.KVStore(storeKeyB) + kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes) + fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB) + require.True(t, equal, "unequal stores: %s / %s:\nstore A %s (%X) => %s (%X)\nstore B %s (%X) => %s (%X)", + storeKeyA, storeKeyB, kvA.Key, kvA.Key, kvA.Value, kvA.Value, kvB.Key, kvB.Key, kvB.Value, kvB.Value) + } + +} + // TODO: Make another test for the fuzzer itself, which just has noOp txs // and doesn't depend on gaia func TestAppStateDeterminism(t *testing.T) { diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index 97d9743a06..cdfc1688c5 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -113,6 +113,6 @@ func genAppStateFromConfig( return } - err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState) + err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) return } diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index f2a815a5c3..a297caee23 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -70,7 +70,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob viper.GetBool(flagOverwrite)); err != nil { return err } - if err = WriteGenesisFile(genFile, chainID, nil, appState); err != nil { + if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil { return err } diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 42d8332d27..8bada583e2 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -298,7 +298,7 @@ func collectGenFiles( genFile := config.GenesisFile() // overwrite each validator's genesis file to have a canonical genesis time - err = WriteGenesisFileWithTime(genFile, chainID, nil, appState, genTime) + err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime) if err != nil { return err } diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go index 1738433705..58edc9b2a5 100644 --- a/cmd/gaia/init/utils.go +++ b/cmd/gaia/init/utils.go @@ -17,9 +17,9 @@ import ( "github.com/tendermint/tendermint/types" ) -// WriteGenesisFile creates and writes the genesis configuration to disk. An +// ExportGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. -func WriteGenesisFile( +func ExportGenesisFile( genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, ) error { @@ -36,9 +36,9 @@ func WriteGenesisFile( return genDoc.SaveAs(genFile) } -// WriteGenesisFileWithTime creates and writes the genesis configuration to disk. +// ExportGenesisFileWithTime creates and writes the genesis configuration to disk. // An error is returned if building or writing the configuration to file fails. -func WriteGenesisFileWithTime( +func ExportGenesisFileWithTime( genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, genTime time.Time, ) error { diff --git a/docs/spec/staking/hooks.md b/docs/spec/staking/hooks.md index 7d24e32e64..3644155a67 100644 --- a/docs/spec/staking/hooks.md +++ b/docs/spec/staking/hooks.md @@ -7,7 +7,7 @@ The staking module allow for the following hooks to be registered with staking e type StakingHooks interface { OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes - OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted + OnValidatorRemoved(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 4f40a14509..f07fbd3ff2 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -108,7 +108,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) }, } diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 12f5d8d294..e8ddd066d7 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -186,8 +186,8 @@ func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, genState := types.GenesisState{ Accounts: accounts, - POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), - CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), + POWGenesis: pow.ExportGenesis(ctx, app.powKeeper), + CoolGenesis: cool.ExportGenesis(ctx, app.coolKeeper), } appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 2f48a64f34..d095b4c790 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -115,7 +115,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) }, } diff --git a/examples/democoin/x/cool/keeper.go b/examples/democoin/x/cool/keeper.go index f805ca880b..9f46b0209e 100644 --- a/examples/democoin/x/cool/keeper.go +++ b/examples/democoin/x/cool/keeper.go @@ -49,8 +49,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error { return nil } -// WriteGenesis - output the genesis trend -func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { +// ExportGenesis - output the genesis trend +func ExportGenesis(ctx sdk.Context, k Keeper) Genesis { trend := k.GetTrend(ctx) return Genesis{trend} } diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index 1eb40dfb29..904681382c 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -37,7 +37,7 @@ func TestCoolKeeper(t *testing.T) { err := InitGenesis(ctx, keeper, Genesis{"icy"}) require.Nil(t, err) - genesis := WriteGenesis(ctx, keeper) + genesis := ExportGenesis(ctx, keeper) require.Nil(t, err) require.Equal(t, genesis, Genesis{"icy"}) diff --git a/examples/democoin/x/pow/keeper.go b/examples/democoin/x/pow/keeper.go index 38a0d93c6a..6c3bfc4eb8 100644 --- a/examples/democoin/x/pow/keeper.go +++ b/examples/democoin/x/pow/keeper.go @@ -43,8 +43,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error { return nil } -// WriteGenesis for the PoW module -func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { +// ExportGenesis for the PoW module +func ExportGenesis(ctx sdk.Context, k Keeper) Genesis { difficulty, err := k.GetLastDifficulty(ctx) if err != nil { panic(err) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 86ccbc8c01..c8d5406f9c 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -41,7 +41,7 @@ func TestPowKeeperGetSet(t *testing.T) { err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)}) require.Nil(t, err) - genesis := WriteGenesis(ctx, keeper) + genesis := ExportGenesis(ctx, keeper) require.Nil(t, err) require.Equal(t, genesis, Genesis{uint64(1), uint64(0)}) diff --git a/types/stake.go b/types/stake.go index 7b10b17f87..38f1c0c9ba 100644 --- a/types/stake.go +++ b/types/stake.go @@ -115,9 +115,9 @@ type DelegationSet interface { // event hooks for staking validator object type StakingHooks interface { - OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created - OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes - OnValidatorRemoved(ctx Context, valAddr ValAddress) // Must be called when a validator is deleted + OnValidatorCreated(ctx Context, valAddr ValAddress) // Must be called when a validator is created + OnValidatorModified(ctx Context, valAddr ValAddress) // Must be called when a validator's state changes + OnValidatorRemoved(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is deleted OnValidatorBonded(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, consAddr ConsAddress, valAddr ValAddress) // Must be called when a validator begins unbonding diff --git a/types/store.go b/types/store.go index a90dc223dd..4b6e79a767 100644 --- a/types/store.go +++ b/types/store.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "fmt" "io" @@ -176,6 +177,43 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) } +// Compare two KVstores, return either the first key/value pair +// at which they differ and whether or not they are equal, skipping +// value comparison for a set of provided prefixes +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { + iterA := a.Iterator(nil, nil) + iterB := b.Iterator(nil, nil) + count = int64(0) + for { + if !iterA.Valid() && !iterB.Valid() { + break + } + var kvA, kvB cmn.KVPair + if iterA.Valid() { + kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} + iterA.Next() + } + if iterB.Valid() { + kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} + iterB.Next() + } + compareValue := true + for _, prefix := range prefixesToSkip { + if bytes.Equal(kvA.Key[:len(prefix)], prefix) { + compareValue = false + } + } + if !bytes.Equal(kvA.Key, kvB.Key) { + return kvA, kvB, count, false + } + if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + return kvA, kvB, count, false + } + count++ + } + return cmn.KVPair{}, cmn.KVPair{}, count, true +} + // CacheKVStore cache-wraps a KVStore. After calling .Write() on // the CacheKVStore, all previously created CacheKVStores on the // object expire. diff --git a/x/auth/genesis.go b/x/auth/genesis.go new file mode 100644 index 0000000000..abc4fc3aea --- /dev/null +++ b/x/auth/genesis.go @@ -0,0 +1,33 @@ +package auth + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState - all auth state that must be provided at genesis +type GenesisState struct { + CollectedFees sdk.Coins `json:"collected_fees"` // collected fees +} + +// Create a new genesis state +func NewGenesisState(collectedFees sdk.Coins) GenesisState { + return GenesisState{ + CollectedFees: collectedFees, + } +} + +// Return a default genesis state +func DefaultGenesisState() GenesisState { + return NewGenesisState(sdk.Coins{}) +} + +// Init store state from genesis data +func InitGenesis(ctx sdk.Context, keeper FeeCollectionKeeper, data GenesisState) { + keeper.setCollectedFees(ctx, data.CollectedFees) +} + +// ExportGenesis returns a GenesisState for a given context and keeper +func ExportGenesis(ctx sdk.Context, keeper FeeCollectionKeeper) GenesisState { + collectedFees := keeper.GetCollectedFees(ctx) + return NewGenesisState(collectedFees) +} diff --git a/x/auth/mapper.go b/x/auth/keeper.go similarity index 100% rename from x/auth/mapper.go rename to x/auth/keeper.go diff --git a/x/auth/mapper_test.go b/x/auth/keeper_test.go similarity index 100% rename from x/auth/mapper_test.go rename to x/auth/keeper_test.go diff --git a/x/distribution/genesis.go b/x/distribution/genesis.go index 4ee5651a67..3468089162 100644 --- a/x/distribution/genesis.go +++ b/x/distribution/genesis.go @@ -21,11 +21,12 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) { for _, dw := range data.DelegatorWithdrawInfos { keeper.SetDelegatorWithdrawAddr(ctx, dw.DelegatorAddr, dw.WithdrawAddr) } + keeper.SetPreviousProposerConsAddr(ctx, data.PreviousProposer) } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, and validator/delegator distribution info's -func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { feePool := keeper.GetFeePool(ctx) communityTax := keeper.GetCommunityTax(ctx) baseProposerRewards := keeper.GetBaseProposerReward(ctx) @@ -33,6 +34,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { vdis := keeper.GetAllValidatorDistInfos(ctx) ddis := keeper.GetAllDelegationDistInfos(ctx) dwis := keeper.GetAllDelegatorWithdrawInfos(ctx) + pp := keeper.GetPreviousProposerConsAddr(ctx) return NewGenesisState(feePool, communityTax, baseProposerRewards, - bonusProposerRewards, vdis, ddis, dwis) + bonusProposerRewards, vdis, ddis, dwis, pp) } diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go index 804ea52422..8e5a37abe2 100644 --- a/x/distribution/keeper/genesis.go +++ b/x/distribution/keeper/genesis.go @@ -36,12 +36,12 @@ func (k Keeper) GetAllDelegationDistInfos(ctx sdk.Context) (ddis []types.Delegat // Get the set of all delegator-withdraw addresses with no limits, used during genesis dump func (k Keeper) GetAllDelegatorWithdrawInfos(ctx sdk.Context) (dwis []types.DelegatorWithdrawInfo) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) + iterator := sdk.KVStorePrefixIterator(store, DelegatorWithdrawInfoKey) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { dw := types.DelegatorWithdrawInfo{ - DelegatorAddr: sdk.AccAddress(iterator.Key()), + DelegatorAddr: GetDelegatorWithdrawInfoAddress(iterator.Key()), WithdrawAddr: sdk.AccAddress(iterator.Value()), } dwis = append(dwis, dw) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index cdebaf93c4..a4f4353fa7 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -103,7 +103,7 @@ func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { h.k.onValidatorModified(ctx, valAddr) } -func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) { h.k.onValidatorRemoved(ctx, valAddr) } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { diff --git a/x/distribution/keeper/key.go b/x/distribution/keeper/key.go index 466d83c241..443d13079e 100644 --- a/x/distribution/keeper/key.go +++ b/x/distribution/keeper/key.go @@ -44,3 +44,12 @@ func GetDelegationDistInfosKey(delAddr sdk.AccAddress) []byte { func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte { return append(DelegatorWithdrawInfoKey, delAddr.Bytes()...) } + +// gets an address from a delegator's withdraw info key +func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) { + addr := key[1:] + if len(addr) != sdk.AddrLen { + panic("unexpected key length") + } + return sdk.AccAddress(addr) +} diff --git a/x/distribution/types/genesis.go b/x/distribution/types/genesis.go index 528295e5c2..0577af4d33 100644 --- a/x/distribution/types/genesis.go +++ b/x/distribution/types/genesis.go @@ -18,10 +18,11 @@ type GenesisState struct { ValidatorDistInfos []ValidatorDistInfo `json:"validator_dist_infos"` DelegationDistInfos []DelegationDistInfo `json:"delegator_dist_infos"` DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` + PreviousProposer sdk.ConsAddress `json:"previous_proposer"` } func NewGenesisState(feePool FeePool, communityTax, baseProposerReward, bonusProposerReward sdk.Dec, - vdis []ValidatorDistInfo, ddis []DelegationDistInfo, dwis []DelegatorWithdrawInfo) GenesisState { + vdis []ValidatorDistInfo, ddis []DelegationDistInfo, dwis []DelegatorWithdrawInfo, pp sdk.ConsAddress) GenesisState { return GenesisState{ FeePool: feePool, @@ -31,6 +32,7 @@ func NewGenesisState(feePool FeePool, communityTax, baseProposerReward, bonusPro ValidatorDistInfos: vdis, DelegationDistInfos: ddis, DelegatorWithdrawInfos: dwis, + PreviousProposer: pp, } } diff --git a/x/gov/genesis.go b/x/gov/genesis.go index db35f68c8c..1243bf5590 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -8,10 +8,25 @@ import ( // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - StartingProposalID uint64 `json:"starting_proposalID"` - DepositParams DepositParams `json:"deposit_params"` - VotingParams VotingParams `json:"voting_params"` - TallyParams TallyParams `json:"tally_params"` + StartingProposalID uint64 `json:"starting_proposal_id"` + Deposits []DepositWithMetadata `json:"deposits"` + Votes []VoteWithMetadata `json:"votes"` + Proposals []Proposal `json:"proposals"` + DepositParams DepositParams `json:"deposit_params"` + VotingParams VotingParams `json:"voting_params"` + TallyParams TallyParams `json:"tally_params"` +} + +// DepositWithMetadata (just for genesis) +type DepositWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Deposit Deposit `json:"deposit"` +} + +// VoteWithMetadata (just for genesis) +type VoteWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Vote Vote `json:"vote"` } func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState { @@ -52,17 +67,47 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { k.setDepositParams(ctx, data.DepositParams) k.setVotingParams(ctx, data.VotingParams) k.setTallyParams(ctx, data.TallyParams) + for _, deposit := range data.Deposits { + k.setDeposit(ctx, deposit.ProposalID, deposit.Deposit.Depositer, deposit.Deposit) + } + for _, vote := range data.Votes { + k.setVote(ctx, vote.ProposalID, vote.Vote.Voter, vote.Vote) + } + for _, proposal := range data.Proposals { + k.SetProposal(ctx, proposal) + } } -// WriteGenesis - output genesis parameters -func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState { - startingProposalID, _ := k.getNewProposalID(ctx) +// ExportGenesis - output genesis parameters +func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { + startingProposalID, _ := k.peekCurrentProposalID(ctx) depositParams := k.GetDepositParams(ctx) votingParams := k.GetVotingParams(ctx) tallyParams := k.GetTallyParams(ctx) + var deposits []DepositWithMetadata + var votes []VoteWithMetadata + proposals := k.GetProposalsFiltered(ctx, nil, nil, StatusNil, 0) + for _, proposal := range proposals { + proposalID := proposal.GetProposalID() + depositsIterator := k.GetDeposits(ctx, proposalID) + for ; depositsIterator.Valid(); depositsIterator.Next() { + var deposit Deposit + k.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) + deposits = append(deposits, DepositWithMetadata{proposalID, deposit}) + } + votesIterator := k.GetVotes(ctx, proposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + var vote Vote + k.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) + votes = append(votes, VoteWithMetadata{proposalID, vote}) + } + } return GenesisState{ StartingProposalID: startingProposalID, + Deposits: deposits, + Votes: votes, + Proposals: proposals, DepositParams: depositParams, VotingParams: votingParams, TallyParams: tallyParams, diff --git a/x/gov/handler.go b/x/gov/handler.go index 279c4cc6e6..180f7a21a2 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -107,8 +107,8 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { var proposalID uint64 keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) inactiveProposal := keeper.GetProposal(ctx, proposalID) - keeper.RefundDeposits(ctx, proposalID) keeper.DeleteProposal(ctx, proposalID) + keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned) resTags = resTags.AppendTag(tags.Action, tags.ActionProposalDropped) resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) diff --git a/x/mint/genesis.go b/x/mint/genesis.go index 5617685730..ce375d71e5 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -31,9 +31,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetParams(ctx, data.Params) } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, and validator/delegator distribution info's -func WriteGenesis(ctx sdk.Context, keeper Keeper) GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { minter := keeper.GetMinter(ctx) params := keeper.GetParams(ctx) diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 10af155d68..614bf41eb3 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -7,13 +7,25 @@ import ( // GenesisState - all slashing state that must be provided at genesis type GenesisState struct { - Params Params + Params Params + SigningInfos map[string]ValidatorSigningInfo + MissedBlocks map[string][]MissedBlock + SlashingPeriods []ValidatorSlashingPeriod +} + +// MissedBlock +type MissedBlock struct { + Index int64 `json:"index"` + Missed bool `json:"missed"` } // HubDefaultGenesisState - default GenesisState used by Cosmos Hub func DefaultGenesisState() GenesisState { return GenesisState{ - Params: DefaultParams(), + Params: DefaultParams(), + SigningInfos: make(map[string]ValidatorSigningInfo), + MissedBlocks: make(map[string][]MissedBlock), + SlashingPeriods: []ValidatorSlashingPeriod{}, } } @@ -24,5 +36,64 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState, sdata types. keeper.addPubkey(ctx, validator.GetConsPubKey()) } + for addr, info := range data.SigningInfos { + address, err := sdk.ConsAddressFromBech32(addr) + if err != nil { + panic(err) + } + keeper.setValidatorSigningInfo(ctx, address, info) + } + + for addr, array := range data.MissedBlocks { + address, err := sdk.ConsAddressFromBech32(addr) + if err != nil { + panic(err) + } + for _, missed := range array { + keeper.setValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed) + } + } + + for _, slashingPeriod := range data.SlashingPeriods { + keeper.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) + } + keeper.paramspace.SetParamSet(ctx, &data.Params) } + +// ExportGenesis writes the current store values +// to a genesis file, which can be imported again +// with InitGenesis +func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { + var params Params + keeper.paramspace.GetParamSet(ctx, ¶ms) + + signingInfos := make(map[string]ValidatorSigningInfo) + missedBlocks := make(map[string][]MissedBlock) + keeper.iterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) { + bechAddr := address.String() + signingInfos[bechAddr] = info + array := []MissedBlock{} + + keeper.iterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { + array = append(array, MissedBlock{index, missed}) + return false + }) + missedBlocks[bechAddr] = array + + return false + }) + + slashingPeriods := []ValidatorSlashingPeriod{} + keeper.iterateValidatorSlashingPeriods(ctx, func(slashingPeriod ValidatorSlashingPeriod) (stop bool) { + slashingPeriods = append(slashingPeriods, slashingPeriod) + return false + }) + + return GenesisState{ + Params: params, + SigningInfos: signingInfos, + MissedBlocks: missedBlocks, + SlashingPeriods: slashingPeriods, + } +} diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index 10c16d1993..e09f6c566e 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -3,6 +3,8 @@ package slashing import ( "time" + "github.com/tendermint/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -36,6 +38,17 @@ func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddre k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) } +// When a validator is created, add the address-pubkey relation. +func (k Keeper) onValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + validator := k.validatorSet.Validator(ctx, valAddr) + k.addPubkey(ctx, validator.GetConsPubKey()) +} + +// When a validator is removed, delete the address-pubkey relation. +func (k Keeper) onValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) { + k.deleteAddrPubkeyRelation(ctx, crypto.Address(address)) +} + //_________________________________________________________________________________________ // Wrapper struct @@ -60,12 +73,20 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre h.k.onValidatorBeginUnbonding(ctx, consAddr, valAddr) } +// Implements sdk.ValidatorHooks +func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, _ sdk.ValAddress) { + h.k.onValidatorRemoved(ctx, consAddr) +} + +// Implements sdk.ValidatorHooks +func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + h.k.onValidatorCreated(ctx, valAddr) +} + // nolint - unused hooks func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { } -func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} -func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) OnDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} func (h Hooks) OnDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} func (h Hooks) OnDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 4a52ddcb61..fe1531f228 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -4,13 +4,10 @@ import ( "fmt" "time" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" stake "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" ) @@ -174,19 +171,6 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p k.setValidatorSigningInfo(ctx, consAddr, signInfo) } -// AddValidators adds the validators to the keepers validator addr to pubkey mapping. -func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.ValidatorUpdate) { - for i := 0; i < len(vals); i++ { - val := vals[i] - pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey) - if err != nil { - panic(err) - } - k.addPubkey(ctx, pubkey) - } -} - -// TODO: Make a method to remove the pubkey from the map when a validator is unbonded. func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { addr := pubkey.Address() k.setAddrPubkeyRelation(ctx, addr, pubkey) diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index caf1cf3da6..94251ded31 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -34,8 +34,7 @@ func TestHandleDoubleSign(t *testing.T) { operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) @@ -75,9 +74,8 @@ func TestSlashingPeriodCap(t *testing.T) { valConsPubKey, valConsAddr := pks[0], pks[0].Address() got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, valConsPubKey, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) + stake.EndBlocker(ctx, sk) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) @@ -141,8 +139,7 @@ func TestHandleAbsentValidator(t *testing.T) { slh := NewHandler(keeper) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // will exist since the validator has been bonded @@ -298,8 +295,7 @@ func TestHandleNewValidator(t *testing.T) { // Validator created got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}}) require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower()) @@ -333,8 +329,7 @@ func TestHandleAlreadyJailed(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) // 1000 first blocks OK height := int64(0) @@ -386,8 +381,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) // 100 first blocks OK height := int64(0) @@ -400,9 +394,8 @@ func TestValidatorDippingInAndOut(t *testing.T) { newAmt := int64(101) got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], sdk.NewInt(newAmt))) require.True(t, got.IsOK()) - validatorUpdates = stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) require.Equal(t, 2, len(validatorUpdates)) - keeper.AddValidators(ctx, validatorUpdates) validator, _ := sk.GetValidator(ctx, addr) require.Equal(t, sdk.Unbonding, validator.Status) diff --git a/x/slashing/keys.go b/x/slashing/keys.go index 8f4fecc6c8..750e8825f1 100644 --- a/x/slashing/keys.go +++ b/x/slashing/keys.go @@ -20,6 +20,15 @@ func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte { return append(ValidatorSigningInfoKey, v.Bytes()...) } +// extract the address from a validator signing info key +func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { + addr := key[1:] + if len(addr) != sdk.AddrLen { + panic("unexpected key length") + } + return sdk.ConsAddress(addr) +} + // stored by *Tendermint* address (not operator address) func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...) diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 6479be9285..77c437174b 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -20,6 +20,21 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress return } +// Stored by *validator* address (not operator address) +func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorSigningInfoKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + address := GetValidatorSigningInfoAddress(iter.Key()) + var info ValidatorSigningInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &info) + if handler(address, info) { + break + } + } +} + // Stored by *validator* address (not operator address) func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) @@ -40,6 +55,24 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con return } +// Stored by *validator* address (not operator address) +func (k Keeper) iterateValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { + store := ctx.KVStore(k.storeKey) + index := int64(0) + // Array may be sparse + for ; index < k.SignedBlocksWindow(ctx); index++ { + var missed bool + bz := store.Get(GetValidatorMissedBlockBitArrayKey(address, index)) + if bz == nil { + continue + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) + if handler(index, missed) { + break + } + } +} + // Stored by *validator* address (not operator address) func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { store := ctx.KVStore(k.storeKey) diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 026d141a2b..4caf5d7c9d 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -51,6 +51,21 @@ func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk return } +// Iterate over all slashing periods in the store, calling on each +// decode slashing period a provided handler function +// Stop if the provided handler function returns true +func (k Keeper) iterateValidatorSlashingPeriods(ctx sdk.Context, handler func(slashingPeriod ValidatorSlashingPeriod) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + slashingPeriod := k.unmarshalSlashingPeriodKeyValue(iter.Key(), iter.Value()) + if handler(slashingPeriod) { + break + } + } +} + // Stored by validator Tendermint address (not operator address) // This function sets a validator slashing period for a particular validator, // start height, end height, and current slashed-so-far total, or updates diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 2f1113aa13..239ae13d62 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -91,7 +91,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s sk.SetHooks(keeper.Hooks()) require.NotPanics(t, func() { - InitGenesis(ctx, keeper, GenesisState{defaults}, genesis) + InitGenesis(ctx, keeper, GenesisState{defaults, nil, nil, nil}, genesis) }) return ctx, ck, sk, paramstore, keeper diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index a2a2d9f0fe..c6590c94e7 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -19,8 +19,7 @@ func TestBeginBlocker(t *testing.T) { // bond the validator got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt)) require.True(t, got.IsOK()) - validatorUpdates := stake.EndBlocker(ctx, sk) - keeper.AddValidators(ctx, validatorUpdates) + stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 63b038613d..d44055ea84 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -2,13 +2,13 @@ package stake import ( "fmt" + "sort" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/pkg/errors" ) // InitGenesis sets the pool and parameters for the provided keeper and @@ -26,22 +26,33 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.SetPool(ctx, data.Pool) keeper.SetParams(ctx, data.Params) + keeper.SetIntraTxCounter(ctx, data.IntraTxCounter) + keeper.SetLastTotalPower(ctx, data.LastTotalPower) + + // We only need to set this if we're starting from a list of validators, not a state export + setBondIntraTxCounter := true + for _, validator := range data.Validators { + if validator.BondIntraTxCounter != 0 { + setBondIntraTxCounter = false + } + } for i, validator := range data.Validators { - validator.BondIntraTxCounter = int16(i) // set the intra-tx counter to the order the validators are presented + // set the intra-tx counter to the order the validators are presented, if necessary + if setBondIntraTxCounter { + validator.BondIntraTxCounter = int16(i) + } keeper.SetValidator(ctx, validator) - if validator.Tokens.IsZero() { - return res, errors.Errorf("genesis validator cannot have zero pool shares, validator: %v", validator) - } - if validator.DelegatorShares.IsZero() { - return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator) - } - // Manually set indices for the first time keeper.SetValidatorByConsAddr(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool) keeper.OnValidatorCreated(ctx, validator.OperatorAddr) + + // Set timeslice if necessary + if validator.Status == sdk.Unbonding { + keeper.InsertValidatorQueue(ctx, validator) + } } for _, delegation := range data.Bonds { @@ -49,24 +60,56 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.OnDelegationCreated(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr) } + sort.SliceStable(data.UnbondingDelegations[:], func(i, j int) bool { + return data.UnbondingDelegations[i].CreationHeight < data.UnbondingDelegations[j].CreationHeight + }) + for _, ubd := range data.UnbondingDelegations { + keeper.SetUnbondingDelegation(ctx, ubd) + keeper.InsertUnbondingQueue(ctx, ubd) + } + + sort.SliceStable(data.Redelegations[:], func(i, j int) bool { + return data.Redelegations[i].CreationHeight < data.Redelegations[j].CreationHeight + }) + for _, red := range data.Redelegations { + keeper.SetRedelegation(ctx, red) + keeper.InsertRedelegationQueue(ctx, red) + } + res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) return } -// WriteGenesis returns a GenesisState for a given context and keeper. The +// ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, params, validators, and bonds found in // the keeper. -func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { +func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { pool := keeper.GetPool(ctx) params := keeper.GetParams(ctx) + intraTxCounter := keeper.GetIntraTxCounter(ctx) + lastTotalPower := keeper.GetLastTotalPower(ctx) validators := keeper.GetAllValidators(ctx) bonds := keeper.GetAllDelegations(ctx) + var unbondingDelegations []types.UnbondingDelegation + keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) { + unbondingDelegations = append(unbondingDelegations, ubd) + return false + }) + var redelegations []types.Redelegation + keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) { + redelegations = append(redelegations, red) + return false + }) return types.GenesisState{ - Pool: pool, - Params: params, - Validators: validators, - Bonds: bonds, + Pool: pool, + Params: params, + IntraTxCounter: intraTxCounter, + LastTotalPower: lastTotalPower, + Validators: validators, + Bonds: bonds, + UnbondingDelegations: unbondingDelegations, + Redelegations: redelegations, } } @@ -118,11 +161,8 @@ func validateGenesisStateValidators(validators []types.Validator) (err error) { if val.Jailed && val.Status == sdk.Bonded { return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress()) } - if val.Tokens.IsZero() { - return fmt.Errorf("genesis validator cannot have zero pool shares, validator: %v", val) - } - if val.DelegatorShares.IsZero() { - return fmt.Errorf("genesis validator cannot have zero delegator shares, validator: %v", val) + if val.DelegatorShares.IsZero() && val.Status != sdk.Unbonding { + return fmt.Errorf("bonded/unbonded genesis validator cannot have zero delegator shares, validator: %v", val) } addrMap[strKey] = true } diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index 7ee16a4538..3f7295a7a0 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -22,29 +22,28 @@ func TestInitGenesis(t *testing.T) { pool.BondedTokens = sdk.NewDec(2) params := keeper.GetParams(ctx) + validators := make([]Validator, 2) var delegations []Delegation - validators := []Validator{ - NewValidator(sdk.ValAddress(keep.Addrs[0]), keep.PKs[0], Description{Moniker: "hoop"}), - NewValidator(sdk.ValAddress(keep.Addrs[1]), keep.PKs[1], Description{Moniker: "bloop"}), - } - genesisState := types.NewGenesisState(pool, params, validators, delegations) - _, err := InitGenesis(ctx, keeper, genesisState) - require.Error(t, err) - // initialize the validators + validators[0].OperatorAddr = sdk.ValAddress(keep.Addrs[0]) + validators[0].ConsPubKey = keep.PKs[0] + validators[0].Description = Description{Moniker: "hoop"} validators[0].Status = sdk.Bonded validators[0].Tokens = sdk.OneDec() validators[0].DelegatorShares = sdk.OneDec() + validators[1].OperatorAddr = sdk.ValAddress(keep.Addrs[1]) + validators[1].ConsPubKey = keep.PKs[1] + validators[1].Description = Description{Moniker: "bloop"} validators[1].Status = sdk.Bonded validators[1].Tokens = sdk.OneDec() validators[1].DelegatorShares = sdk.OneDec() - genesisState = types.NewGenesisState(pool, params, validators, delegations) + genesisState := types.NewGenesisState(pool, params, validators, delegations) vals, err := InitGenesis(ctx, keeper, genesisState) require.NoError(t, err) - actualGenesis := WriteGenesis(ctx, keeper) + actualGenesis := ExportGenesis(ctx, keeper) require.Equal(t, genesisState.Pool, actualGenesis.Pool) require.Equal(t, genesisState.Params, actualGenesis.Params) require.Equal(t, genesisState.Bonds, actualGenesis.Bonds) @@ -126,10 +125,6 @@ func TestValidateGenesis(t *testing.T) { (*data).Validators = genValidators1 (*data).Validators = append((*data).Validators, genValidators1[0]) }, true}, - {"no pool shares", func(data *types.GenesisState) { - (*data).Validators = genValidators1 - (*data).Validators[0].Tokens = sdk.ZeroDec() - }, true}, {"no delegator shares", func(data *types.GenesisState) { (*data).Validators = genValidators1 (*data).Validators[0].DelegatorShares = sdk.ZeroDec() diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index fbb62dcbff..d535419e13 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -283,6 +283,21 @@ func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) { store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr), []byte{}) } +// iterate through all redelegations +func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, RedelegationKey) + defer iterator.Close() + + for i := int64(0); iterator.Valid(); iterator.Next() { + red := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + if stop := fn(i, red); stop { + break + } + i++ + } +} + // remove a redelegation object and associated index func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { store := ctx.KVStore(k.storeKey) diff --git a/x/stake/keeper/hooks.go b/x/stake/keeper/hooks.go index 4a8496ddd2..74e8304906 100644 --- a/x/stake/keeper/hooks.go +++ b/x/stake/keeper/hooks.go @@ -17,9 +17,9 @@ func (k Keeper) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { } } -func (k Keeper) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) { +func (k Keeper) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { - k.hooks.OnValidatorRemoved(ctx, valAddr) + k.hooks.OnValidatorRemoved(ctx, consAddr, valAddr) } } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index 307270c16f..3f98f08c56 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -191,11 +191,13 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. validator, pool = validator.UpdateStatus(pool, sdk.Bonded) k.SetPool(ctx, pool) - // save the now bonded validator record to the three referenced stores + // save the now bonded validator record to the two referenced stores k.SetValidator(ctx, validator) - k.SetValidatorByPowerIndex(ctx, validator, pool) + // delete from queue if present + k.DeleteValidatorQueue(ctx, validator) + // call the bond hook if present if k.hooks != nil { k.hooks.OnValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr) @@ -224,9 +226,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) validator.UnbondingHeight = ctx.BlockHeader().Height - // save the now unbonded validator record + // save the now unbonded validator record and power index k.SetValidator(ctx, validator) - k.SetValidatorByPowerIndex(ctx, validator, pool) // Adds to unbonding validator queue diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 4646480ac6..c7919537cf 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "container/list" "fmt" "time" @@ -201,6 +202,11 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) + // call hook if present + if k.hooks != nil { + k.hooks.OnValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr) + } + } //___________________________________________________________________________ @@ -320,6 +326,12 @@ func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, store.Set(GetValidatorQueueTimeKey(timestamp), bz) } +// Deletes a specific validator queue timeslice. +func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetValidatorQueueTimeKey(timestamp)) +} + // Insert an validator address to the appropriate timeslice in the validator queue func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) @@ -331,6 +343,22 @@ func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { } } +// Delete a validator address from the validator queue +func (k Keeper) DeleteValidatorQueue(ctx sdk.Context, val types.Validator) { + timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) + newTimeSlice := []sdk.ValAddress{} + for _, addr := range timeSlice { + if !bytes.Equal(addr, val.OperatorAddr) { + newTimeSlice = append(newTimeSlice, addr) + } + } + if len(newTimeSlice) == 0 { + k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingMinTime) + } else { + k.SetValidatorQueueTimeSlice(ctx, val.UnbondingMinTime, newTimeSlice) + } +} + // Returns all the validator queue timeslices from time 0 until endTime func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(k.storeKey) @@ -358,9 +386,12 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) for _, valAddr := range timeslice { val, found := k.GetValidator(ctx, valAddr) - if !found || val.GetStatus() != sdk.Unbonding { + if !found { continue } + if val.GetStatus() != sdk.Unbonding { + panic("unexpected validator in unbonding queue, status was not unbonding") + } k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() { k.RemoveValidator(ctx, val.OperatorAddr) diff --git a/x/stake/stake.go b/x/stake/stake.go index 9d76177228..87087e59c0 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -57,6 +57,9 @@ var ( GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey TestingUpdateValidator = keeper.TestingUpdateValidator + UnbondingQueueKey = keeper.UnbondingQueueKey + RedelegationQueueKey = keeper.RedelegationQueueKey + ValidatorQueueKey = keeper.ValidatorQueueKey DefaultParamspace = keeper.DefaultParamspace KeyUnbondingTime = types.KeyUnbondingTime diff --git a/x/stake/types/genesis.go b/x/stake/types/genesis.go index d08c6b8993..f1673a376a 100644 --- a/x/stake/types/genesis.go +++ b/x/stake/types/genesis.go @@ -1,11 +1,19 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` - Validators []Validator `json:"validators"` - Bonds []Delegation `json:"bonds"` + Pool Pool `json:"pool"` + Params Params `json:"params"` + IntraTxCounter int16 `json:"intra_tx_counter"` + LastTotalPower sdk.Int `json:"last_total_power"` + Validators []Validator `json:"validators"` + Bonds []Delegation `json:"bonds"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations"` } func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState { From 02da39789923c948a4b91b34dda275d46381cc90 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 9 Nov 2018 01:41:57 +0100 Subject: [PATCH 60/61] Merge 0.26.0 back to develop (#2718) * PENDING => CHANGELOG * Linkify changelog * Merge PR #2716: Temporarily disable gaia lite insecure mode * TODO: need to update CHANGELOG w/ import-export PR #2690 --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++ PENDING.md | 24 +------------------- client/lcd/root.go | 37 ++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e01163cf..8be464b400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,60 @@ # Changelog +## 0.26.0 + +BREAKING CHANGES + +* Gaia + * [gaiad init] [\#2602](https://github.com/cosmos/cosmos-sdk/issues/2602) New genesis workflow + +* SDK + * [simulation] [\#2665](https://github.com/cosmos/cosmos-sdk/issues/2665) only argument to simulation.Invariant is now app + +* Tendermint + * Upgrade to version 0.26.0 + +FEATURES + +* Gaia CLI (`gaiacli`) + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations + * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. + * [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction. + +* SDK + * [\#1336](https://github.com/cosmos/cosmos-sdk/issues/1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. + +IMPROVEMENTS + +* Gaia + * [\#2637](https://github.com/cosmos/cosmos-sdk/issues/2637) [x/gov] Switched inactive and active proposal queues to an iterator based queue + +* SDK + * [\#2573](https://github.com/cosmos/cosmos-sdk/issues/2573) [x/distribution] add accum invariance + * [\#2556](https://github.com/cosmos/cosmos-sdk/issues/2556) [x/mock/simulation] Fix debugging output + * [\#2396](https://github.com/cosmos/cosmos-sdk/issues/2396) [x/mock/simulation] Change parameters to get more slashes + * [\#2617](https://github.com/cosmos/cosmos-sdk/issues/2617) [x/mock/simulation] Randomize all genesis parameters + * [\#2669](https://github.com/cosmos/cosmos-sdk/issues/2669) [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. + * [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) [x/mock/simulation] Use a transition matrix for block size + * [\#2660](https://github.com/cosmos/cosmos-sdk/issues/2660) [x/mock/simulation] Staking transactions get tested far more frequently + * [\#2610](https://github.com/cosmos/cosmos-sdk/issues/2610) [x/stake] Block redelegation to and from the same validator + * [\#2652](https://github.com/cosmos/cosmos-sdk/issues/2652) [x/auth] Add benchmark for get and set account + * [\#2685](https://github.com/cosmos/cosmos-sdk/issues/2685) [store] Add general merkle absence proof (also for empty substores) + * [\#2708](https://github.com/cosmos/cosmos-sdk/issues/2708) [store] Disallow setting nil values + +BUG FIXES + +* Gaia + * [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + * [\#2691](https://github.com/cosmos/cosmos-sdk/issues/2691) Fix local testnet creation by using a single canonical genesis time + +* SDK + * [\#2625](https://github.com/cosmos/cosmos-sdk/issues/2625) [x/gov] fix AppendTag function usage error + * [\#2677](https://github.com/cosmos/cosmos-sdk/issues/2677) [x/stake, x/distribution] various staking/distribution fixes as found by the simulator + * [\#2674](https://github.com/cosmos/cosmos-sdk/issues/2674) [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see [\#2686](https://github.com/cosmos/cosmos-sdk/issues/2686)) + * [\#2711](https://github.com/cosmos/cosmos-sdk/issues/2711) [x/stake] Add commission data to `MsgCreateValidator` signature bytes. + * Temporarily disable insecure mode for Gaia Lite + ## 0.25.0 *October 24th, 2018* diff --git a/PENDING.md b/PENDING.md index 1d5752fc2d..0b56d5333e 100644 --- a/PENDING.md +++ b/PENDING.md @@ -7,23 +7,17 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * Gaia - * [gaiad init] \#2602 New genesis workflow * SDK - * [simulation] \#2665 only argument to simulation.Invariant is now app * Tendermint - * Upgrade to version 0.26.0 + FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations - * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations - * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. - * [cli] [\#2558](https://github.com/cosmos/cosmos-sdk/issues/2558) Rename --print-sigs to --validate-signatures. It now performs a complete set of sanity checks and reports to the user. Also added --print-signature-only to print the signature only, not the whole transaction. * Gaia @@ -40,20 +34,8 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia - - #2637 [x/gov] Switched inactive and active proposal queues to an iterator based queue * SDK - - \#2573 [x/distribution] add accum invariance - - \#2556 [x/mock/simulation] Fix debugging output - - \#2396 [x/mock/simulation] Change parameters to get more slashes - - \#2617 [x/mock/simulation] Randomize all genesis parameters - - \#2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - - \#1924 [x/mock/simulation] Use a transition matrix for block size - - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - - \#2610 [x/stake] Block redelegation to and from the same validator - - \#2652 [x/auth] Add benchmark for get and set account - - \#2685 [store] Add general merkle absence proof (also for empty substores) - - \#2708 [store] Disallow setting nil values * Tendermint @@ -70,9 +52,5 @@ BUG FIXES - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK - - \#2625 [x/gov] fix AppendTag function usage error - - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator - - \#2674 [types] Fix coin.IsLT() impl, coins.IsLT() impl, and renamed coins.Is\* to coins.IsAll\* (see \#2686) - - \#2711 [x/stake] Add commission data to `MsgCreateValidator` signature bytes. * Tendermint diff --git a/client/lcd/root.go b/client/lcd/root.go index c6e7917e7b..8366b6114c 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -2,6 +2,11 @@ package lcd import ( "errors" + "fmt" + "net" + "net/http" + "os" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" @@ -20,9 +25,6 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" tmserver "github.com/tendermint/tendermint/rpc/lib/server" - "net" - "net/http" - "os" ) const ( @@ -46,7 +48,9 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) (err error) { listenAddr := viper.GetString(flagListenAddr) handler := createHandler(cdc) + registerSwaggerUI(handler) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") maxOpen := viper.GetInt(flagMaxOpenConnections) sslHosts := viper.GetString(flagSSLHosts) @@ -62,14 +66,20 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { }) var cleanupFunc func() + + // TODO: re-enable insecure mode once #2715 has been addressed if viper.GetBool(flagInsecure) { - listener, err = tmserver.StartHTTPServer( - listenAddr, handler, logger, - tmserver.Config{MaxOpenConnections: maxOpen}, + fmt.Println( + "Insecure mode is temporarily disabled, please locally generate an " + + "SSL certificate to test. Support will be re-enabled soon!", ) - if err != nil { - return - } + // listener, err = tmserver.StartHTTPServer( + // listenAddr, handler, logger, + // tmserver.Config{MaxOpenConnections: maxOpen}, + // ) + // if err != nil { + // return + // } } else { if certFile != "" { // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 @@ -77,6 +87,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return err } + // cert/key pair is provided, read the fingerprint fingerprint, err = fingerprintFromFile(certFile) if err != nil { @@ -88,10 +99,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return err } + cleanupFunc = func() { os.Remove(certFile) os.Remove(keyFile) } + defer cleanupFunc() } @@ -104,9 +117,12 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { if err != nil { return } + logger.Info(fingerprint) + logger.Info("REST server started") } - logger.Info("REST server started") + + // logger.Info("REST server started") return nil }, @@ -123,6 +139,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) From d8b6d1d1062877ec2edd2237bf07b360d5cbc9e5 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Thu, 8 Nov 2018 16:45:44 -0800 Subject: [PATCH 61/61] Update CHANGELOG/PENDING for straggling PR #2690 --- CHANGELOG.md | 2 ++ PENDING.md | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be464b400..f635b7ea6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ BUG FIXES * Gaia * [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrect `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * [\#2691](https://github.com/cosmos/cosmos-sdk/issues/2691) Fix local testnet creation by using a single canonical genesis time + - [\#2670](https://github.com/cosmos/cosmos-sdk/issues/2670) [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - [\#2648](https://github.com/cosmos/cosmos-sdk/issues/2648) [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI * SDK * [\#2625](https://github.com/cosmos/cosmos-sdk/issues/2625) [x/gov] fix AppendTag function usage error diff --git a/PENDING.md b/PENDING.md index 0b56d5333e..4d87301f0d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -22,7 +22,6 @@ FEATURES * Gaia * SDK - - \#1336 Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint @@ -47,9 +46,6 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` - - \#2648 [gaiad] Fix `gaiad export` / `gaiad import` consistency, test in CI - - \#2691 Fix local testnet creation by using a single canonical genesis time * SDK