From 4f0c3cb25a3a2f74d29555254699b3115f4ef0a3 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 11 Sep 2018 02:12:35 -0700 Subject: [PATCH 01/13] Merge PR #2291: x/staking: standardize validator record not found error --- x/stake/keeper/validator.go | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index d2411de72..c628dbea8 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -158,9 +158,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat } address := GetAddressFromValBondedIndexKey(iterator.Key()) validator, found := k.GetValidator(ctx, address) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", address)) - } + ensureValidatorFound(found, address) validators[i] = validator i++ @@ -184,9 +182,8 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { } address := iterator.Value() validator, found := k.GetValidator(ctx, address) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", address)) - } + ensureValidatorFound(found, address) + if validator.Status == sdk.Bonded { validators[i] = validator i++ @@ -327,9 +324,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato if iterator.Valid() { ownerAddr := iterator.Value() currVal, found := k.GetValidator(ctx, ownerAddr) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) - } + ensureValidatorFound(found, ownerAddr) if currVal.Status != sdk.Bonded || currVal.Jailed { panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %s\n", ownerAddr)) @@ -454,9 +449,7 @@ func (k Keeper) UpdateBondedValidators( } else { var found bool validator, found = k.GetValidator(ctx, ownerAddr) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) - } + ensureValidatorFound(found, ownerAddr) } // if we've reached jailed validators no further bonded validators exist @@ -502,9 +495,7 @@ func (k Keeper) UpdateBondedValidators( if newValidatorBonded { if oldCliffValidatorAddr != nil { oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr)) - } + ensureValidatorFound(found, oldCliffValidatorAddr) if bytes.Equal(validatorToBond.OperatorAddr, affectedValidator.OperatorAddr) { @@ -560,9 +551,7 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { ownerAddr := iterator.Value() validator, found = k.GetValidator(ctx, ownerAddr) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) - } + ensureValidatorFound(found, ownerAddr) _, found = toKickOut[string(ownerAddr)] if found { @@ -605,9 +594,7 @@ func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) { for key := range toKickOut { ownerAddr := []byte(key) validator, found := k.GetValidator(ctx, ownerAddr) - if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) - } + ensureValidatorFound(found, ownerAddr) k.beginUnbondingValidator(ctx, validator) } } @@ -738,3 +725,9 @@ func (k Keeper) clearCliffValidator(ctx sdk.Context) { store.Delete(ValidatorPowerCliffKey) store.Delete(ValidatorCliffIndexKey) } + +func ensureValidatorFound(found bool, ownerAddr []byte) { + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr)) + } +} From 2e0fc15dd4b0d58f5ff0264b4ad573e484c7045f Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 11 Sep 2018 02:14:32 -0700 Subject: [PATCH 02/13] Merge PR #2292: x/staking: Use variable names in switch case --- x/stake/keeper/validator.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index c628dbea8..c5fe3def7 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -243,6 +243,11 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator) valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool) cliffPower := k.GetCliffValidatorPower(ctx) + cliffValExists := (cliffPower != nil) + var valPowerLTcliffPower bool + if cliffValExists { + valPowerLTcliffPower = (bytes.Compare(valPower, cliffPower) == -1) + } switch { @@ -256,7 +261,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bz) - if cliffPower != nil { + if cliffValExists { cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx)) if bytes.Equal(cliffAddr, validator.OperatorAddr) { k.updateCliffValidator(ctx, validator) @@ -264,14 +269,13 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type } // if is a new validator and the new power is less than the cliff validator - case cliffPower != nil && !oldFound && - bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower + case cliffValExists && !oldFound && valPowerLTcliffPower: // skip to completion // if was unbonded and the new power is less than the cliff validator - case cliffPower != nil && + case cliffValExists && (oldFound && oldValidator.Status == sdk.Unbonded) && - bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower + valPowerLTcliffPower: //(valPower < cliffPower // skip to completion default: From 2a49406e9b686bc64e74547d5ec9152b8ef73cbc Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 11 Sep 2018 02:15:38 -0700 Subject: [PATCH 03/13] Merge PR #2284: Prevent create_empty_blocks=false --- PENDING.md | 1 + server/util.go | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/PENDING.md b/PENDING.md index 331d1fc85..1acffb10a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -97,6 +97,7 @@ IMPROVEMENTS * [x/stake] [x/slashing] Ensure delegation invariants to jailed validators [#1883](https://github.com/cosmos/cosmos-sdk/issues/1883). * [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200) * [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state. + * Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571 * SDK * [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present. diff --git a/server/util.go b/server/util.go index 04c539eb4..6aff52965 100644 --- a/server/util.go +++ b/server/util.go @@ -51,6 +51,10 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error if err != nil { return err } + err = validateConfig(config) + if err != nil { + return err + } logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) if err != nil { @@ -96,6 +100,14 @@ func interceptLoadConfig() (conf *cfg.Config, err error) { return } +// validate the config with the sdk's requirements. +func validateConfig(conf *cfg.Config) error { + if conf.Consensus.CreateEmptyBlocks == false { + return errors.New("config option CreateEmptyBlocks = false is currently unsupported") + } + return nil +} + // add server commands func AddCommands( ctx *Context, cdc *wire.Codec, From 962b04d98522cddfa94de5ba751a7de34973b01c Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 11 Sep 2018 02:18:58 -0700 Subject: [PATCH 04/13] Merge PR #2285: simulation: Write logs on panic --- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 9 +++- x/mock/simulation/random_simulate_blocks.go | 48 ++++++++++++++------- x/mock/simulation/util.go | 29 +++++++++++-- 4 files changed, 67 insertions(+), 20 deletions(-) diff --git a/PENDING.md b/PENDING.md index 1acffb10a..b82f34f2e 100644 --- a/PENDING.md +++ b/PENDING.md @@ -107,6 +107,7 @@ IMPROVEMENTS * [store] \#1952, \#2281 Update IAVL dependency to v0.11.0 * [simulation] Make timestamps randomized [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) * [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282 + * [simulation] Logs get written to file if large, and also get printed on panics \#2285 * Tendermint diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index e809495fc..497aa1383 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -131,7 +131,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { // Run randomized simulation // TODO parameterize numbers, save for a later PR - simulation.SimulateFromSeed( + err := simulation.SimulateFromSeed( b, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []simulation.RandSetup{}, @@ -140,6 +140,10 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { blockSize, commit, ) + if err != nil { + fmt.Println(err) + b.Fail() + } if commit { fmt.Println("GoLevelDB Stats") fmt.Println(db.Stats()["leveldb.stats"]) @@ -164,7 +168,7 @@ func TestFullGaiaSimulation(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - simulation.SimulateFromSeed( + err := simulation.SimulateFromSeed( t, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []simulation.RandSetup{}, @@ -176,6 +180,7 @@ func TestFullGaiaSimulation(t *testing.T) { if commit { fmt.Println("Database Size", db.Stats()["database.size"]) } + require.Nil(t, err) } // TODO: Make another test for the fuzzer itself, which just has noOp txs diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 08b70d101..e4a263e23 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -7,6 +7,7 @@ import ( "math/rand" "os" "os/signal" + "runtime/debug" "sort" "strings" "syscall" @@ -26,9 +27,9 @@ import ( func Simulate( t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []Operation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool, -) { +) error { time := time.Now().UnixNano() - SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) + return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } func initChain(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress, setups []RandSetup, app *baseapp.BaseApp, @@ -56,7 +57,9 @@ func randTimestamp(r *rand.Rand) time.Time { func SimulateFromSeed( tb testing.TB, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []Operation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool, -) { +) (simError error) { + // in case we have to end early, don't os.Exit so that we can run cleanup code. + stopEarly := false testingMode, t, b := getTestingMode(tb) fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) r := rand.New(rand.NewSource(seed)) @@ -79,12 +82,12 @@ func SimulateFromSeed( // Setup code to catch SIGTERM's c := make(chan os.Signal) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) + signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) go func() { - <-c - fmt.Printf("Exiting early due to SIGTERM, on block %d, operation %d\n", header.Height, opCount) - DisplayEvents(events) - os.Exit(128 + int(syscall.SIGTERM)) + receivedSignal := <-c + fmt.Printf("Exiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) + simError = fmt.Errorf("Exited due to %s", receivedSignal) + stopEarly = true }() var pastTimes []time.Time @@ -95,15 +98,27 @@ func SimulateFromSeed( operationQueue := make(map[int][]Operation) var blockLogBuilders []*strings.Builder - if !testingMode { - b.ResetTimer() - } else { + if testingMode { blockLogBuilders = make([]*strings.Builder, numBlocks) } displayLogs := logPrinter(testingMode, blockLogBuilders) blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, numBlocks, displayLogs) + if !testingMode { + b.ResetTimer() + } else { + // Recover logs in case of panic + defer func() { + if r := recover(); r != nil { + fmt.Println("Panic with err\n", r) + stackTrace := string(debug.Stack()) + fmt.Println(stackTrace) + displayLogs() + simError = fmt.Errorf("Simulation halted due to panic on block %d", header.Height) + } + }() + } - for i := 0; i < numBlocks; i++ { + for i := 0; i < numBlocks && !stopEarly; i++ { // Log the header time for future lookup pastTimes = append(pastTimes, header.Time) pastSigningValidators = append(pastSigningValidators, request.LastCommitInfo.Validators) @@ -122,10 +137,9 @@ func SimulateFromSeed( // Run queued operations. Ignores blocksize if blocksize is too small numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event) - opCount += numQueuedOpsRan thisBlockSize -= numQueuedOpsRan operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter) - opCount += operations + opCount += operations + numQueuedOpsRan res := app.EndBlock(abci.RequestEndBlock{}) header.Height++ @@ -146,9 +160,13 @@ func SimulateFromSeed( // Update the validator set validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event) } - + if stopEarly { + DisplayEvents(events) + return + } fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds), : %v, operations ran %d\n", header.Height, header.Time, opCount) DisplayEvents(events) + return nil } // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index d4e1ca1a7..a46d6dd54 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -3,9 +3,11 @@ package simulation import ( "fmt" "math/rand" + "os" "sort" "strings" "testing" + "time" "github.com/tendermint/tendermint/crypto" @@ -94,13 +96,34 @@ func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invari func logPrinter(testingmode bool, logs []*strings.Builder) func() { if testingmode { return func() { + numLoggers := 0 for i := 0; i < len(logs); i++ { // We're passed the last created block if logs[i] == nil { - return + numLoggers = i - 1 + break + } + } + var f *os.File + if numLoggers > 10 { + fileName := fmt.Sprintf("simulation_log_%s.txt", time.Now().Format("2006-01-02 15:04:05")) + fmt.Printf("Too many logs to display, instead writing to %s\n", fileName) + f, _ = os.Create(fileName) + } + for i := 0; i < numLoggers; i++ { + if f != nil { + _, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i)) + if err != nil { + panic("Failed to write logs to file") + } + _, err = f.WriteString((*logs[i]).String()) + if err != nil { + panic("Failed to write logs to file") + } + } else { + fmt.Printf("Begin block %d\n", i) + fmt.Println((*logs[i]).String()) } - fmt.Printf("Begin block %d\n", i) - fmt.Println((*logs[i]).String()) } } } From fb0cc0b07845e093482c68c1a55714943a1ede43 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 12 Sep 2018 01:31:30 +0100 Subject: [PATCH 05/13] Merge PR #2306: Change --gas=0 semantic and introduce --gas=simulate * Change --gas=0 semantic and introduce --gas=simulate Make --gas flag accept a conventional "simulate" string value in addition to integers. Passing --gas=simulate would trigger the tx simulation and set the gas according to the gas estimate returned by the simulation. Any other integer value passed to --gas would be interpreted as-is and and set as gas wanted value. Closes: #2300 * Add test cases with gas=0 * ACK suggestion from @alexanderbez * s/GasFlagSimulateString/GasFlagSimulate/ * Drop TODO comment on Gas type * Enrich TODO with ref --- PENDING.md | 5 ++- client/context/context.go | 10 ----- client/flags.go | 58 ++++++++++++++++++++++++++-- client/lcd/lcd_test.go | 28 +++++++++----- client/utils/utils.go | 28 +++++++------- cmd/gaia/cli_test/cli_test.go | 12 +++++- docs/light/api.md | 4 +- docs/sdk/clients.md | 2 +- x/auth/client/txbuilder/txbuilder.go | 8 +++- x/bank/client/rest/sendtx.go | 30 ++++++++------ x/gov/client/rest/util.go | 31 ++++++++------- x/ibc/client/rest/transfer.go | 25 +++++++----- x/slashing/client/rest/tx.go | 12 +++--- x/stake/client/rest/tx.go | 34 +++++++++------- 14 files changed, 184 insertions(+), 103 deletions(-) diff --git a/PENDING.md b/PENDING.md index b82f34f2e..aec65f3d5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -61,8 +61,9 @@ FEATURES * [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in * [\#2040](https://github.com/cosmos/cosmos-sdk/issues/2040) Add `--bech` to `gaiacli keys show` and respective REST endpoint to provide desired Bech32 prefix encoding - * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution. - * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0. + * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) [\#2306](https://github.com/cosmos/cosmos-sdk/pull/2306) Passing --gas=simulate triggers a simulation of the tx before the actual execution. + The gas estimate obtained via the simulation will be used as gas limit in the actual execution. + * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=simulate. * [cli] [\#2110](https://github.com/cosmos/cosmos-sdk/issues/2110) Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated. * [cli] [\#2204](https://github.com/cosmos/cosmos-sdk/issues/2204) Support generating and broadcasting messages with multiple signatures via command line: * [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. diff --git a/client/context/context.go b/client/context/context.go index a4d4afd3b..3e785a28e 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -27,8 +27,6 @@ type CLIContext struct { Client rpcclient.Client Logger io.Writer Height int64 - Gas int64 - GasAdjustment float64 NodeURI string FromAddressName string AccountStore string @@ -58,8 +56,6 @@ func NewCLIContext() CLIContext { AccountStore: ctxAccStoreName, FromAddressName: viper.GetString(client.FlagFrom), Height: viper.GetInt64(client.FlagHeight), - Gas: viper.GetInt64(client.FlagGas), - GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment), TrustNode: viper.GetBool(client.FlagTrustNode), UseLedger: viper.GetBool(client.FlagUseLedger), Async: viper.GetBool(client.FlagAsync), @@ -164,9 +160,3 @@ func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext { ctx.Certifier = certifier return ctx } - -// WithGasAdjustment returns a copy of the context with an updated GasAdjustment flag. -func (ctx CLIContext) WithGasAdjustment(adjustment float64) CLIContext { - ctx.GasAdjustment = adjustment - return ctx -} diff --git a/client/flags.go b/client/flags.go index 97fce42a5..c98898029 100644 --- a/client/flags.go +++ b/client/flags.go @@ -1,6 +1,11 @@ package client -import "github.com/spf13/cobra" +import ( + "fmt" + "strconv" + + "github.com/spf13/cobra" +) // nolint const ( @@ -9,6 +14,7 @@ const ( // occur between the tx simulation and the actual run. DefaultGasAdjustment = 1.0 DefaultGasLimit = 200000 + GasFlagSimulate = "simulate" FlagUseLedger = "ledger" FlagChainID = "chain-id" @@ -32,7 +38,10 @@ const ( // LineBreak can be included in a command list to provide a blank line // to help with readability -var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} +var ( + LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} + GasFlagVar = GasSetting{Gas: DefaultGasLimit} +) // GetCommands adds common flags to query commands func GetCommands(cmds ...*cobra.Command) []*cobra.Command { @@ -58,7 +67,6 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") - c.Flags().Int64(FlagGas, DefaultGasLimit, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") @@ -66,6 +74,50 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses") c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it") c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT") + // --gas can accept integers and "simulate" + c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf( + "gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", GasFlagSimulate, DefaultGasLimit)) } return cmds } + +// Gas flag parsing functions + +// GasSetting encapsulates the possible values passed through the --gas flag. +type GasSetting struct { + Simulate bool + Gas int64 +} + +// Type returns the flag's value type. +func (v *GasSetting) Type() string { return "string" } + +// Set parses and sets the value of the --gas flag. +func (v *GasSetting) Set(s string) (err error) { + v.Simulate, v.Gas, err = ReadGasFlag(s) + return +} + +func (v *GasSetting) String() string { + if v.Simulate { + return GasFlagSimulate + } + return strconv.FormatInt(v.Gas, 10) +} + +// ParseGasFlag parses the value of the --gas flag. +func ReadGasFlag(s string) (simulate bool, gas int64, err error) { + switch s { + case "": + gas = DefaultGasLimit + case GasFlagSimulate: + simulate = true + default: + gas, err = strconv.ParseInt(s, 10, 64) + if err != nil { + err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate) + return + } + } + return +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 977c03cf7..b3c27bfba 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -267,21 +267,29 @@ func TestCoinSend(t *testing.T) { require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, "") + require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) + + // test failure with negative gas + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "") + require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) + + // test failure with 0 gas + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "") require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with wrong adjustment - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 0.1, "") require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?simulate=true") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 0, "?simulate=true") require.Equal(t, http.StatusOK, res.StatusCode, body) var responseBody struct { GasEstimate int64 `json:"gas_estimate"` } require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, "") require.Equal(t, http.StatusOK, res.StatusCode, body) } @@ -322,7 +330,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?generate_only=true") + res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, "?generate_only=true") require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -792,7 +800,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { return acc } -func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) { +func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address kb := client.MockKeyBase() @@ -811,14 +819,14 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc } gasStr := "" - if gas > 0 { + if len(gas) != 0 { gasStr = fmt.Sprintf(` - "gas":"%v", + "gas":%q, `, gas) } gasAdjustmentStr := "" if gasAdjustment > 0 { - gasStr = fmt.Sprintf(` + gasAdjustmentStr = fmt.Sprintf(` "gas_adjustment":"%v", `, gasAdjustment) } @@ -837,7 +845,7 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc } func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { - res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "") + res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "") require.Equal(t, http.StatusOK, res.StatusCode, body) err := cdc.UnmarshalJSON([]byte(body), &resultTx) diff --git a/client/utils/utils.go b/client/utils/utils.go index 54d9dd584..52472ba1e 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -24,8 +24,8 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) if err != nil { return err } - autogas := cliCtx.DryRun || (cliCtx.Gas == 0) - if autogas { + + if txBldr.SimulateGas || cliCtx.DryRun { txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) if err != nil { return err @@ -50,20 +50,10 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) return cliCtx.EnsureBroadcastTx(txBytes) } -// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func SimulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg, gas int64) (estimated, adjusted int64, err error) { - txBytes, err := txBldr.WithGas(gas).BuildWithPubKey(name, msgs) - if err != nil { - return - } - estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - return -} - // EnrichCtxWithGas calculates the gas estimate that would be consumed by the // transaction and set the transaction's respective value accordingly. func EnrichCtxWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) { - _, adjusted, err := SimulateMsgs(txBldr, cliCtx, name, msgs, 0) + _, adjusted, err := simulateMsgs(txBldr, cliCtx, name, msgs) if err != nil { return txBldr, err } @@ -143,6 +133,16 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, return txBldr.SignStdTx(name, passphrase, stdTx, appendSig) } +// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. +func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted int64, err error) { + txBytes, err := txBldr.BuildWithPubKey(name, msgs) + if err != nil { + return + } + estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, txBldr.GasAdjustment) + return +} + func adjustGasEstimate(estimate int64, adjustment float64) int64 { return int64(adjustment * float64(estimate)) } @@ -194,7 +194,7 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg if err != nil { return } - if txBldr.Gas == 0 { + if txBldr.SimulateGas { txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) if err != nil { return diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 032482e7b..43f35925e 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -112,8 +112,16 @@ func TestGaiaCLIGasAuto(t *testing.T) { fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + // Test failure with negative gas + success = executeWrite(t, fmt.Sprintf("gaiacli send %v --gas=-100 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + require.False(t, success) + + // Test failure with 0 gas + success = executeWrite(t, fmt.Sprintf("gaiacli send %v --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + require.False(t, success) + // Enable auto gas - success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli send %v --json --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli send %v --json --gas=simulate --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) require.True(t, success) // check that gas wanted == gas used cdc := app.MakeCodec() @@ -381,7 +389,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Test generate sendTx, estimate gas success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf( - "gaiacli send %v --amount=10steak --to=%s --from=foo --gas=0 --generate-only", + "gaiacli send %v --amount=10steak --to=%s --from=foo --gas=simulate --generate-only", flags, barAddr), []string{}...) require.True(t, success) require.NotEmpty(t, stderr) diff --git a/docs/light/api.md b/docs/light/api.md index 6c7f9de17..7fbf9fbe1 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -763,7 +763,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te "chain_id": "string", "account_number": 0, "sequence": 0, - "gas": 0 + "gas": "simulate" }, "depositer": "string", "amount": 0, @@ -866,7 +866,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te "chain_id": "string", "account_number": 0, "sequence": 0, - "gas": 0 + "gas": "simulate" }, // A cosmos address "voter": "string", diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 4d02d3c90..5b7d3ca86 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -111,7 +111,7 @@ The `--amount` flag accepts the format `--amount=`. ::: tip Note You may want to cap the maximum gas that can be consumed by the transaction via the `--gas` flag. -If set to 0, the gas limit will be automatically estimated. +If you pass `--gas=simulate`, the gas limit will be automatically estimated. Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.0. ::: diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 026eabece..6daa75e12 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -16,7 +16,9 @@ type TxBuilder struct { Codec *wire.Codec AccountNumber int64 Sequence int64 - Gas int64 + Gas int64 // TODO: should this turn into uint64? requires further discussion - see #2173 + GasAdjustment float64 + SimulateGas bool ChainID string Memo string Fee string @@ -36,9 +38,11 @@ func NewTxBuilderFromCLI() TxBuilder { return TxBuilder{ ChainID: chainID, - Gas: viper.GetInt64(client.FlagGas), AccountNumber: viper.GetInt64(client.FlagAccountNumber), + Gas: client.GasFlagVar.Gas, + GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment), Sequence: viper.GetInt64(client.FlagSequence), + SimulateGas: client.GasFlagVar.Simulate, Fee: viper.GetString(client.FlagFee), Memo: viper.GetString(client.FlagMemo), } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 82df24642..02a66f2b9 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -32,7 +32,7 @@ type sendBody struct { ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` - Gas int64 `json:"gas"` + Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` } @@ -81,31 +81,37 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo return } - txBldr := authtxb.TxBuilder{ - Codec: cdc, - Gas: m.Gas, - ChainID: m.ChainID, - AccountNumber: m.AccountNumber, - Sequence: m.Sequence, + simulateGas, gas, err := cliclient.ReadGasFlag(m.Gas) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, cliclient.DefaultGasAdjustment) if !ok { return } - cliCtx = cliCtx.WithGasAdjustment(adjustment) + txBldr := authtxb.TxBuilder{ + Codec: cdc, + Gas: gas, + GasAdjustment: adjustment, + SimulateGas: simulateGas, + ChainID: m.ChainID, + AccountNumber: m.AccountNumber, + Sequence: m.Sequence, + } - if utils.HasDryRunArg(r) || m.Gas == 0 { - newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) + if utils.HasDryRunArg(r) || txBldr.SimulateGas { + newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if utils.HasDryRunArg(r) { - utils.WriteSimulationResponse(w, txBldr.Gas) + utils.WriteSimulationResponse(w, newBldr.Gas) return } - txBldr = newCtx + txBldr = newBldr } if utils.HasGenerateOnlyArg(r) { diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index f9987c030..b152cb20b 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -20,7 +20,7 @@ type baseReq struct { ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` - Gas int64 `json:"gas"` + Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` } @@ -69,32 +69,37 @@ func (req baseReq) baseReqValidate(w http.ResponseWriter) bool { // TODO: Build this function out into a more generic base-request // (probably should live in client/lcd). func signAndBuild(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) { - var err error - txBldr := authtxb.TxBuilder{ - Codec: cdc, - AccountNumber: baseReq.AccountNumber, - Sequence: baseReq.Sequence, - ChainID: baseReq.ChainID, - Gas: baseReq.Gas, + simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) if !ok { return } - cliCtx = cliCtx.WithGasAdjustment(adjustment) + txBldr := authtxb.TxBuilder{ + Codec: cdc, + Gas: gas, + GasAdjustment: adjustment, + SimulateGas: simulateGas, + ChainID: baseReq.ChainID, + AccountNumber: baseReq.AccountNumber, + Sequence: baseReq.Sequence, + } - if utils.HasDryRunArg(r) || baseReq.Gas == 0 { - newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg}) + if utils.HasDryRunArg(r) || txBldr.SimulateGas { + newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if utils.HasDryRunArg(r) { - utils.WriteSimulationResponse(w, txBldr.Gas) + utils.WriteSimulationResponse(w, newBldr.Gas) return } - txBldr = newCtx + txBldr = newBldr } if utils.HasGenerateOnlyArg(r) { diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 110efe601..5a7b632e8 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -29,7 +29,7 @@ type transferBody struct { SrcChainID string `json:"src_chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` - Gas int64 `json:"gas"` + Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` } @@ -71,21 +71,26 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C packet := ibc.NewIBCPacket(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount, m.SrcChainID, destChainID) msg := ibc.IBCTransferMsg{packet} - txBldr := authtxb.TxBuilder{ - Codec: cdc, - ChainID: m.SrcChainID, - AccountNumber: m.AccountNumber, - Sequence: m.Sequence, - Gas: m.Gas, + simulateGas, gas, err := client.ReadGasFlag(m.Gas) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } - adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment) if !ok { return } - cliCtx = cliCtx.WithGasAdjustment(adjustment) + txBldr := authtxb.TxBuilder{ + Codec: cdc, + Gas: gas, + GasAdjustment: adjustment, + SimulateGas: simulateGas, + ChainID: m.SrcChainID, + AccountNumber: m.AccountNumber, + Sequence: m.Sequence, + } - if utils.HasDryRunArg(r) || m.Gas == 0 { + if utils.HasDryRunArg(r) || txBldr.SimulateGas { newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 1d4fdefa1..969cdcce0 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -70,22 +70,20 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI return } + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } txBldr := authtxb.TxBuilder{ Codec: cdc, ChainID: m.ChainID, AccountNumber: m.AccountNumber, Sequence: m.Sequence, Gas: m.Gas, + GasAdjustment: adjustment, } msg := slashing.NewMsgUnjail(valAddr) - - adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment) - if !ok { - return - } - cliCtx = cliCtx.WithGasAdjustment(adjustment) - if utils.HasDryRunArg(r) || m.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index d84a8daea..fda92f85a 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -60,7 +60,7 @@ type EditDelegationsBody struct { ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` - Gas int64 `json:"gas"` + Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` Delegations []msgDelegationsInput `json:"delegations"` BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` @@ -263,10 +263,21 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex i++ } + simulateGas, gas, err := client.ReadGasFlag(m.Gas) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } txBldr := authtxb.TxBuilder{ - Codec: cdc, - ChainID: m.ChainID, - Gas: m.Gas, + Codec: cdc, + Gas: gas, + GasAdjustment: adjustment, + SimulateGas: simulateGas, + ChainID: m.ChainID, } // sign messages @@ -275,26 +286,19 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex // increment sequence for each message txBldr = txBldr.WithAccountNumber(m.AccountNumber) txBldr = txBldr.WithSequence(m.Sequence) - m.Sequence++ - adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment) - if !ok { - return - } - cliCtx = cliCtx.WithGasAdjustment(adjustment) - - if utils.HasDryRunArg(r) || m.Gas == 0 { - newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) + if utils.HasDryRunArg(r) || txBldr.SimulateGas { + newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if utils.HasDryRunArg(r) { - utils.WriteSimulationResponse(w, txBldr.Gas) + utils.WriteSimulationResponse(w, newBldr.Gas) return } - txBldr = newCtx + txBldr = newBldr } if utils.HasGenerateOnlyArg(r) { From 358b48771cb406a6edc02e25ab81c87f20d129ac Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 12 Sep 2018 00:13:14 -0700 Subject: [PATCH 06/13] Merge PR #2308: Remove ripemd160 entirely * Remove ripemd160 entirely We already made this decision awhile ago, and have had tendermint switched for awhile. I was surprised to find ripemd still used within the storeinfo. This actually leads me to think the new "byter" API change in the tendermint PR RFC compliance is better, as it avoids things like this from ever happening. * Get ripemd160 removed from the gopkg imports --- Gopkg.lock | 1 - PENDING.md | 1 + examples/README.md | 2 +- store/multistoreproof.go | 2 +- store/multistoreproof_test.go | 9 +++++---- store/rootmultistore.go | 5 ++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index b647d2021..d5ccc6589 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -679,7 +679,6 @@ "github.com/tendermint/tmlibs/cli", "github.com/zondax/ledger-goclient", "golang.org/x/crypto/blowfish", - "golang.org/x/crypto/ripemd160", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/PENDING.md b/PENDING.md index aec65f3d5..652da599b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -43,6 +43,7 @@ BREAKING CHANGES * [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282 * [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211) * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) + * [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308 * Tendermint diff --git a/examples/README.md b/examples/README.md index 11aef9d28..5d872a7c9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -253,7 +253,7 @@ first time. Accounts are serialized and stored in a Merkle tree under the key ``base/a/
``, where ``
`` is the address of the account. -Typically, the address of the account is the 20-byte ``RIPEMD160`` hash +Typically, the address of the account is the first 20-bytes of the ``sha256`` hash of the public key, but other formats are acceptable as well, as defined in the `Tendermint crypto library `__. The Merkle tree diff --git a/store/multistoreproof.go b/store/multistoreproof.go index e25f1cc1f..d62bc4aca 100644 --- a/store/multistoreproof.go +++ b/store/multistoreproof.go @@ -2,6 +2,7 @@ package store import ( "bytes" + "github.com/pkg/errors" "github.com/tendermint/iavl" cmn "github.com/tendermint/tendermint/libs/common" @@ -47,7 +48,6 @@ func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHas Version: height, StoreInfos: storeInfos, } - if !bytes.Equal(appHash, ci.Hash()) { return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash") } diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index 790588fb2..45a102cd3 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -5,13 +5,14 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/iavl" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/db" ) func TestVerifyMultiStoreCommitInfo(t *testing.T) { - appHash, _ := hex.DecodeString("ebf3c1fb724d3458023c8fefef7b33add2fc1e84") + appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") storeName := "acc" @@ -83,13 +84,13 @@ func TestVerifyMultiStoreCommitInfo(t *testing.T) { }) commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) - assert.Nil(t, err) - assert.Equal(t, commitHash, substoreRootHash) + require.Nil(t, err) + require.Equal(t, commitHash, substoreRootHash) appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3") _, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) - assert.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo") + require.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo") } func TestVerifyRangeProof(t *testing.T) { diff --git a/store/rootmultistore.go b/store/rootmultistore.go index d9cf8a29a..8aa2da0ba 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -5,10 +5,9 @@ import ( "io" "strings" - "golang.org/x/crypto/ripemd160" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/crypto/tmhash" dbm "github.com/tendermint/tendermint/libs/db" sdk "github.com/cosmos/cosmos-sdk/types" @@ -424,7 +423,7 @@ 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 - hasher := ripemd160.New() + hasher := tmhash.New() _, err := hasher.Write(bz) if err != nil { // TODO: Handle with #870 From 55b7c6adf11f4949793b44c9755cfa409c478eb4 Mon Sep 17 00:00:00 2001 From: Jia Chenhui Date: Wed, 12 Sep 2018 15:14:29 +0800 Subject: [PATCH 07/13] Merge PR #2304: client/keys: remove excess code and uniform code style --- client/keys/show.go | 86 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/client/keys/show.go b/client/keys/show.go index 9710cac11..c7cae1f7c 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -5,9 +5,7 @@ import ( "fmt" "net/http" - "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/gorilla/mux" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -19,7 +17,7 @@ const ( FlagAddress = "address" // FlagPublicKey represents the user's public key on the command line. FlagPublicKey = "pubkey" - // FlagBechPrefix defines a desired Bech32 prefix encoding for a key + // FlagBechPrefix defines a desired Bech32 prefix encoding for a key. FlagBechPrefix = "bech" ) @@ -29,39 +27,7 @@ func showKeysCmd() *cobra.Command { Short: "Show key info for the given name", Long: `Return public details of one local key.`, Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - name := args[0] - info, err := getKey(name) - if err != nil { - return err - } - - showAddress := viper.GetBool(FlagAddress) - showPublicKey := viper.GetBool(FlagPublicKey) - outputSet := cmd.Flag(cli.OutputFlag).Changed - - if showAddress && showPublicKey { - return errors.New("cannot use both --address and --pubkey at once") - } - if outputSet && (showAddress || showPublicKey) { - return errors.New("cannot use --output with --address or --pubkey") - } - - bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix)) - if err != nil { - return err - } - - switch { - case showAddress: - printKeyAddress(info, bechKeyOut) - case showPublicKey: - printPubKey(info, bechKeyOut) - default: - printKeyInfo(info, bechKeyOut) - } - return nil - }, + RunE: runShowCmd, } cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)") @@ -71,6 +37,43 @@ func showKeysCmd() *cobra.Command { return cmd } +func runShowCmd(cmd *cobra.Command, args []string) error { + name := args[0] + + info, err := GetKeyInfo(name) + if err != nil { + return err + } + + isShowAddr := viper.GetBool(FlagAddress) + isShowPubKey := viper.GetBool(FlagPublicKey) + isOutputSet := cmd.Flag(cli.OutputFlag).Changed + + if isShowAddr && isShowPubKey { + return errors.New("cannot use both --address and --pubkey at once") + } + + if isOutputSet && (isShowAddr || isShowPubKey) { + return errors.New("cannot use --output with --address or --pubkey") + } + + bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix)) + if err != nil { + return err + } + + switch { + case isShowAddr: + printKeyAddress(info, bechKeyOut) + case isShowPubKey: + printPubKey(info, bechKeyOut) + default: + printKeyInfo(info, bechKeyOut) + } + + return nil +} + func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { switch bechPrefix { case "acc": @@ -84,15 +87,6 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix) } -func getKey(name string) (keys.Info, error) { - kb, err := GetKeyBase() - if err != nil { - return nil, err - } - - return kb.Get(name) -} - /////////////////////////// // REST @@ -113,7 +107,7 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) { return } - info, err := getKey(name) + info, err := GetKeyInfo(name) // TODO: check for the error if key actually does not exist, instead of // assuming this as the reason if err != nil { From 854aca2f7dd486263fcbf338e251de4f3c61c279 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 12 Sep 2018 07:16:28 +0000 Subject: [PATCH 08/13] Merge PR #2238: Ensure Tendermint Validator Update Invariants --- PENDING.md | 2 + docs/spec/staking/end_block.md | 2 +- x/gov/simulation/msgs.go | 6 + x/mock/simulation/random_simulate_blocks.go | 15 +- x/stake/handler.go | 2 +- x/stake/keeper/validator.go | 71 +++--- x/stake/keeper/validator_test.go | 232 +++++++++++++++++--- 7 files changed, 258 insertions(+), 72 deletions(-) diff --git a/PENDING.md b/PENDING.md index 652da599b..dd4acbe41 100644 --- a/PENDING.md +++ b/PENDING.md @@ -122,6 +122,8 @@ BUG FIXES * [cli] [\#2265](https://github.com/cosmos/cosmos-sdk/issues/2265) Fix JSON formatting of the `gaiacli send` command. * Gaia + * [x/stake] Return correct Tendermint validator update set on `EndBlocker` by not + including non previously bonded validators that have zero power. [#2189](https://github.com/cosmos/cosmos-sdk/issues/2189) * SDK * [\#1988](https://github.com/cosmos/cosmos-sdk/issues/1988) Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988) diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index c2fe143ba..a29469261 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -12,7 +12,7 @@ the changes cleared ```golang EndBlock() ValidatorSetChanges - vsc = GetTendermintUpdates() + vsc = GetValidTendermintUpdates() ClearTendermintUpdates() return vsc ``` diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index 399f73512..3eb21b79a 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -156,13 +156,16 @@ func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation { return operationSimulateMsgVote(k, sk, nil, -1) } + // nolint: unparam func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { if key == nil { key = simulation.RandomKey(r, keys) } + var ok bool + if proposalID < 0 { proposalID, ok = randomProposalID(r, k, ctx) if !ok { @@ -171,15 +174,18 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, } addr := sdk.AccAddress(key.PubKey().Address()) option := randomVotingOption(r) + msg := gov.NewMsgVote(addr, proposalID, option) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } + ctx, write := ctx.CacheContext() result := gov.NewHandler(k)(ctx, msg) if result.IsOK() { write() } + event(fmt.Sprintf("gov/MsgVote/%v", result.IsOK())) action = fmt.Sprintf("TestMsgVote: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) return action, nil, nil diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index e4a263e23..3f91b2a66 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -342,18 +342,14 @@ 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.Validator, event func(string)) map[string]mockValidator { + for _, update := range updates { switch { case update.Power == 0: - // // TEMPORARY DEBUG CODE TO PROVE THAT THE OLD METHOD WAS BROKEN - // // (i.e. didn't catch in the event of problem) - // if val, ok := tb.(*testing.T); ok { - // require.NotNil(val, current[string(update.PubKey.Data)]) - // } - // // CORRECT CHECK - // if _, ok := current[string(update.PubKey.Data)]; !ok { - // tb.Fatalf("tried to delete a nonexistent validator") - // } + if _, ok := current[string(update.PubKey.Data)]; !ok { + tb.Fatalf("tried to delete a nonexistent validator") + } + event("endblock/validatorupdates/kicked") delete(current, string(update.PubKey.Data)) default: @@ -368,5 +364,6 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida } } } + return current } diff --git a/x/stake/handler.go b/x/stake/handler.go index e6ceb5e7b..b00db571d 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid k.SetIntraTxCounter(ctx, 0) // calculate validator set changes - ValidatorUpdates = k.GetTendermintUpdates(ctx) + ValidatorUpdates = k.GetValidTendermintUpdates(ctx) k.ClearTendermintUpdates(ctx) return } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index c5fe3def7..aaed74be4 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -198,16 +198,37 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { // Accumulated updates to the active/bonded validator set for tendermint // get the most recently updated validators -func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) { +// +// CONTRACT: Only validators with non-zero power or zero-power that were bonded +// at the previous block height or were removed from the validator set entirely +// are returned to Tendermint. +func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest + iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) for ; iterator.Valid(); iterator.Next() { - valBytes := iterator.Value() - var val abci.Validator - k.cdc.MustUnmarshalBinary(valBytes, &val) - updates = append(updates, val) + var abciVal abci.Validator + + abciValBytes := iterator.Value() + k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal) + + val, found := k.GetValidator(ctx, abciVal.GetAddress()) + if found { + // The validator is new or already exists in the store and must adhere to + // Tendermint invariants. + prevBonded := val.BondHeight < ctx.BlockHeight() && val.BondHeight > val.UnbondingHeight + zeroPower := val.GetPower().Equal(sdk.ZeroDec()) + + if !zeroPower || zeroPower && prevBonded { + updates = append(updates, abciVal) + } + } else { + // Add the ABCI validator in such a case where the validator was removed + // from the store as it must have existed before. + updates = append(updates, abciVal) + } } + iterator.Close() return } @@ -240,7 +261,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type validator = k.updateForJailing(ctx, oldFound, oldValidator, validator) powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator) - validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator) + validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator) valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool) cliffPower := k.GetCliffValidatorPower(ctx) cliffValExists := (cliffPower != nil) @@ -316,7 +337,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato oldCliffVal, found := k.GetValidator(ctx, cliffAddr) if !found { - panic(fmt.Sprintf("cliff validator record not found for address: %v\n", cliffAddr)) + panic(fmt.Sprintf("cliff validator record not found for address: %X\n", cliffAddr)) } // Create a validator iterator ranging from smallest to largest by power @@ -331,7 +352,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato ensureValidatorFound(found, ownerAddr) if currVal.Status != sdk.Bonded || currVal.Jailed { - panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %s\n", ownerAddr)) + panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr)) } newCliffVal = currVal @@ -344,13 +365,10 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool) if bytes.Equal(affectedVal.OperatorAddr, newCliffVal.OperatorAddr) { - // The affected validator remains the cliff validator, however, since // the store does not contain the new power, update the new power rank. store.Set(ValidatorPowerCliffKey, affectedValRank) - } else if bytes.Compare(affectedValRank, newCliffValRank) > 0 { - // The affected validator no longer remains the cliff validator as it's // power is greater than the new cliff validator. k.setCliffValidator(ctx, newCliffVal, pool) @@ -381,18 +399,20 @@ func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, // get the bond height and incremented intra-tx counter // nolint: unparam -func (k Keeper) bondIncrement(ctx sdk.Context, oldFound bool, oldValidator, - newValidator types.Validator) (height int64, intraTxCounter int16) { +func (k Keeper) bondIncrement( + ctx sdk.Context, found bool, oldValidator types.Validator) (height int64, intraTxCounter int16) { - // if already a validator, copy the old block height and counter, else set them - if oldFound && oldValidator.Status == sdk.Bonded { + // if already a validator, copy the old block height and counter + if found && oldValidator.Status == sdk.Bonded { height = oldValidator.BondHeight intraTxCounter = oldValidator.BondIntraTxCounter return } + height = ctx.BlockHeight() counter := k.GetIntraTxCounter(ctx) intraTxCounter = counter + k.SetIntraTxCounter(ctx, counter+1) return } @@ -458,22 +478,20 @@ func (k Keeper) UpdateBondedValidators( // if we've reached jailed validators no further bonded validators exist if validator.Jailed { - break - } - - // increment bondedValidatorsCount / get the validator to bond - if validator.Status != sdk.Bonded { - validatorToBond = validator - if newValidatorBonded { - panic("already decided to bond a validator, can't bond another!") + if validator.Status == sdk.Bonded { + panic(fmt.Sprintf("jailed validator cannot be bonded, address: %X\n", ownerAddr)) } - newValidatorBonded = true + + break } // increment the total number of bonded validators and potentially mark // the validator to bond if validator.Status != sdk.Bonded { validatorToBond = validator + if newValidatorBonded { + panic("already decided to bond a validator, can't bond another!") + } newValidatorBonded = true } @@ -507,7 +525,6 @@ func (k Keeper) UpdateBondedValidators( // validator was newly bonded and has greater power k.beginUnbondingValidator(ctx, oldCliffVal) } else { - // otherwise begin unbonding the affected validator, which must // have been kicked out affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator) @@ -653,6 +670,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator)) } + validator.BondHeight = ctx.BlockHeight() + // set the status validator, pool = validator.UpdateStatus(pool, sdk.Bonded) k.SetPool(ctx, pool) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 87fbcbc65..d9531ae1a 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -16,8 +16,11 @@ func TestSetValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 10) pool := keeper.GetPool(ctx) + valPubKey := PKs[0] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + // test how the validator is set from a purely unbonbed pool - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := types.NewValidator(valAddr, valPubKey, types.Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, sdk.Unbonded, validator.Status) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) @@ -26,14 +29,14 @@ func TestSetValidator(t *testing.T) { keeper.UpdateValidator(ctx, validator) // after the save the validator should be bonded - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := keeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) // Check each store for being saved - resVal, found := keeper.GetValidator(ctx, addrVals[0]) + resVal, found := keeper.GetValidator(ctx, valAddr) assert.True(ValEq(t, validator, resVal)) require.True(t, found) @@ -45,7 +48,7 @@ func TestSetValidator(t *testing.T) { require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validator, resVals[0])) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validator.ABCIValidator(), updates[0]) } @@ -641,38 +644,47 @@ func TestClearTendermintUpdates(t *testing.T) { validators := make([]types.Validator, len(amts)) for i, amt := range amts { pool := keeper.GetPool(ctx) - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + valPubKey := PKs[i] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + keeper.SetPool(ctx, pool) keeper.UpdateValidator(ctx, validators[i]) } - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, len(amts), len(updates)) keeper.ClearTendermintUpdates(ctx) - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 0, len(updates)) } -func TestGetTendermintUpdatesAllNone(t *testing.T) { +func TestGetValidTendermintUpdatesAllNone(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} var validators [2]types.Validator for i, amt := range amts { pool := keeper.GetPool(ctx) - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } // test from nothing to something // tendermintUpdate set: {} -> {c1, c3} - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) assert.Equal(t, 2, len(updates)) assert.Equal(t, validators[0].ABCIValidator(), updates[0]) assert.Equal(t, validators[1].ABCIValidator(), updates[1]) @@ -680,12 +692,12 @@ func TestGetTendermintUpdatesAllNone(t *testing.T) { // test from something to nothing // tendermintUpdate set: {} -> {c1, c2, c3, c4} keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) keeper.RemoveValidator(ctx, validators[0].OperatorAddr) keeper.RemoveValidator(ctx, validators[1].OperatorAddr) - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.GetValidTendermintUpdates(ctx) assert.Equal(t, 2, len(updates)) assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].ConsPubKey), updates[0].PubKey) assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].ConsPubKey), updates[1].PubKey) @@ -693,7 +705,7 @@ func TestGetTendermintUpdatesAllNone(t *testing.T) { assert.Equal(t, int64(0), updates[1].Power) } -func TestGetTendermintUpdatesIdentical(t *testing.T) { +func TestGetValidTendermintUpdatesIdentical(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -707,16 +719,16 @@ func TestGetTendermintUpdatesIdentical(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test identical, // tendermintUpdate set: {} -> {} validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) } -func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { +func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -730,7 +742,7 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test single value change // tendermintUpdate set: {} -> {c1'} @@ -738,13 +750,13 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { validators[0].Tokens = sdk.NewDec(600) validators[0] = keeper.UpdateValidator(ctx, validators[0]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[0].ABCIValidator(), updates[0]) } -func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { +func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -758,7 +770,7 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} @@ -769,13 +781,13 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 2, len(updates)) require.Equal(t, validators[0].ABCIValidator(), updates[0]) require.Equal(t, validators[1].ABCIValidator(), updates[1]) } -func TestGetTendermintUpdatesInserted(t *testing.T) { +func TestGetValidTendermintUpdatesInserted(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20, 5, 15, 25} @@ -789,12 +801,12 @@ func TestGetTendermintUpdatesInserted(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} validators[2] = keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[2].ABCIValidator(), updates[0]) @@ -802,7 +814,7 @@ func TestGetTendermintUpdatesInserted(t *testing.T) { // tendermintUpdate set: {} -> {c0} keeper.ClearTendermintUpdates(ctx) validators[3] = keeper.UpdateValidator(ctx, validators[3]) - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[3].ABCIValidator(), updates[0]) @@ -810,12 +822,12 @@ func TestGetTendermintUpdatesInserted(t *testing.T) { // tendermintUpdate set: {} -> {c0} keeper.ClearTendermintUpdates(ctx) validators[4] = keeper.UpdateValidator(ctx, validators[4]) - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[4].ABCIValidator(), updates[0]) } -func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) { +func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := types.DefaultParams() params.MaxValidators = 2 @@ -832,31 +844,31 @@ func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test validator added at the end but not inserted in the valset // tendermintUpdate set: {} -> {} keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 0, len(updates)) // test validator change its power and become a gotValidator (pushing out an existing) // tendermintUpdate set: {} -> {c0, c4} keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) pool := keeper.GetPool(ctx) validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(10)) keeper.SetPool(ctx, pool) validators[2] = keeper.UpdateValidator(ctx, validators[2]) - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 2, len(updates), "%v", updates) require.Equal(t, validators[0].ABCIValidatorZero(), updates[0]) require.Equal(t, validators[2].ABCIValidator(), updates[1]) } -func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { +func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{100, 100} @@ -870,7 +882,7 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // check initial power require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64()) @@ -890,8 +902,158 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64()) // Tendermint updates should reflect power change - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 2, len(updates)) require.Equal(t, validators[0].ABCIValidator(), updates[0]) require.Equal(t, validators[1].ABCIValidator(), updates[1]) } + +func TestGetValidTendermintUpdatesNewValidator(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 1000) + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(3) + + keeper.SetParams(ctx, params) + + amts := []int64{100, 100} + var validators [2]types.Validator + + // initialize some validators into the state + for i, amt := range amts { + pool := keeper.GetPool(ctx) + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + keeper.SetPool(ctx, pool) + validators[i] = keeper.UpdateValidator(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := keeper.GetValidTendermintUpdates(ctx) + require.Equal(t, len(validators), len(updates)) + require.Equal(t, validators[0].ABCIValidator(), updates[0]) + require.Equal(t, validators[1].ABCIValidator(), updates[1]) + + keeper.ClearTendermintUpdates(ctx) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + + // update initial validator set + for i, amt := range amts { + pool := keeper.GetPool(ctx) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + keeper.SetPool(ctx, pool) + validators[i] = keeper.UpdateValidator(ctx, validators[i]) + } + + // add a new validator that goes from zero power, to non-zero power, back to + // zero power + pool := keeper.GetPool(ctx) + valPubKey := PKs[len(validators)+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + amt := sdk.NewInt(100) + + validator := types.NewValidator(valAddr, valPubKey, types.Description{}) + validator, pool, _ = validator.AddTokensFromDel(pool, amt) + + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + + validator, pool, _ = validator.RemoveDelShares(pool, sdk.NewDecFromInt(amt)) + validator = keeper.UpdateValidator(ctx, validator) + + // add a new validator that increases in power + valPubKey = PKs[len(validators)+2] + valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) + + validator = types.NewValidator(valAddr, valPubKey, types.Description{}) + validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500)) + + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + + // verify initial Tendermint updates are correct + updates = keeper.GetValidTendermintUpdates(ctx) + require.Equal(t, len(validators)+1, len(updates)) + require.Equal(t, validator.ABCIValidator(), updates[0]) + require.Equal(t, validators[0].ABCIValidator(), updates[1]) + require.Equal(t, validators[1].ABCIValidator(), updates[2]) +} + +func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 1000) + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(2) + + keeper.SetParams(ctx, params) + + amts := []int64{100, 200, 300} + var validators [3]types.Validator + + // initialize some validators into the state + for i, amt := range amts { + pool := keeper.GetPool(ctx) + moniker := fmt.Sprintf("%d", i) + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker}) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + keeper.SetPool(ctx, pool) + validators[i] = keeper.UpdateValidator(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := keeper.GetValidTendermintUpdates(ctx) + require.Equal(t, 2, len(updates)) + require.Equal(t, validators[2].ABCIValidator(), updates[0]) + require.Equal(t, validators[1].ABCIValidator(), updates[1]) + + keeper.ClearTendermintUpdates(ctx) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + + // delegate to validator with lowest power but not enough to bond + ctx = ctx.WithBlockHeight(1) + pool := keeper.GetPool(ctx) + + validator, found := keeper.GetValidator(ctx, validators[0].OperatorAddr) + require.True(t, found) + + validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(1)) + + keeper.SetPool(ctx, pool) + validators[0] = keeper.UpdateValidator(ctx, validator) + + // verify initial Tendermint updates are correct + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + + // create a series of events that will bond and unbond the validator with + // lowest power in a single block context (height) + ctx = ctx.WithBlockHeight(2) + pool = keeper.GetPool(ctx) + + validator, found = keeper.GetValidator(ctx, validators[1].OperatorAddr) + require.True(t, found) + + validator, pool, _ = validator.RemoveDelShares(pool, validator.DelegatorShares) + + keeper.SetPool(ctx, pool) + validator = keeper.UpdateValidator(ctx, validator) + + validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(250)) + + keeper.SetPool(ctx, pool) + validators[1] = keeper.UpdateValidator(ctx, validator) + + // verify initial Tendermint updates are correct + updates = keeper.GetValidTendermintUpdates(ctx) + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[1].ABCIValidator(), updates[0]) + + keeper.ClearTendermintUpdates(ctx) + require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) +} From e5e7c4fa0fcbbbd807cc3d63db6145c00acba92a Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 12 Sep 2018 00:16:52 -0700 Subject: [PATCH 09/13] Merge PR #2303: simulation: Add weighted operation --- Makefile | 2 +- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 26 ++++++++++----------- x/bank/simulation/sim_test.go | 4 ++-- x/gov/simulation/sim_test.go | 14 +++++------ x/mock/simulation/random_simulate_blocks.go | 23 ++++++++++++++---- x/mock/simulation/types.go | 7 ++++++ x/stake/simulation/sim_test.go | 16 ++++++------- 8 files changed, 58 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 88ead03e6..ee76c6d19 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,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=200 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -v -timeout 24h test_sim_gaia_slow: @echo "Running full Gaia simulation. This may take awhile!" diff --git a/PENDING.md b/PENDING.md index dd4acbe41..d1d25c827 100644 --- a/PENDING.md +++ b/PENDING.md @@ -109,6 +109,7 @@ IMPROVEMENTS * [store] \#1952, \#2281 Update IAVL dependency to v0.11.0 * [simulation] Make timestamps randomized [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) * [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282 + * [simulation] Add a concept of weighting the operations \#2303 * [simulation] Logs get written to file if large, and also get printed on panics \#2285 * Tendermint diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 497aa1383..b12f3978c 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -89,19 +89,19 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json return appState } -func testAndRunTxs(app *GaiaApp) []simulation.Operation { - return []simulation.Operation{ - banksim.SimulateSingleInputMsgSend(app.accountMapper), - govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper), - govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper), - stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper), - stakesim.SimulateMsgEditValidator(app.stakeKeeper), - stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper), - stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper), - stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper), - stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper), - stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper), - slashingsim.SimulateMsgUnjail(app.slashingKeeper), +func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { + return []simulation.WeightedOperation{ + {100, banksim.SimulateSingleInputMsgSend(app.accountMapper)}, + {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, + {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, + {100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)}, + {5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)}, + {100, stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper)}, + {100, stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper)}, + {100, stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper)}, + {100, stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper)}, + {100, stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper)}, + {100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)}, } } diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index f0279abef..555dc9a6c 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -33,8 +33,8 @@ func TestBankWithRandomMessages(t *testing.T) { simulation.Simulate( t, mapp.BaseApp, appStateFn, - []simulation.Operation{ - SimulateSingleInputMsgSend(mapper), + []simulation.WeightedOperation{ + {1, SimulateSingleInputMsgSend(mapper)}, }, []simulation.RandSetup{}, []simulation.Invariant{ diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index b7f0be580..411dbf0c8 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -56,10 +56,10 @@ func TestGovWithRandomMessages(t *testing.T) { // Test with unscheduled votes simulation.Simulate( t, mapp.BaseApp, appStateFn, - []simulation.Operation{ - SimulateMsgSubmitProposal(govKeeper, stakeKeeper), - SimulateMsgDeposit(govKeeper, stakeKeeper), - SimulateMsgVote(govKeeper, stakeKeeper), + []simulation.WeightedOperation{ + {2, SimulateMsgSubmitProposal(govKeeper, stakeKeeper)}, + {3, SimulateMsgDeposit(govKeeper, stakeKeeper)}, + {20, SimulateMsgVote(govKeeper, stakeKeeper)}, }, []simulation.RandSetup{ setup, }, []simulation.Invariant{ @@ -71,9 +71,9 @@ func TestGovWithRandomMessages(t *testing.T) { // Test with scheduled votes simulation.Simulate( t, mapp.BaseApp, appStateFn, - []simulation.Operation{ - SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper), - SimulateMsgDeposit(govKeeper, stakeKeeper), + []simulation.WeightedOperation{ + {10, SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper)}, + {5, SimulateMsgDeposit(govKeeper, stakeKeeper)}, }, []simulation.RandSetup{ setup, }, []simulation.Invariant{ diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 3f91b2a66..c5bbd2ac1 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -25,7 +25,7 @@ import ( // Simulate tests application by sending random messages. func Simulate( - t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []Operation, setups []RandSetup, + t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool, ) error { time := time.Now().UnixNano() @@ -55,7 +55,7 @@ func randTimestamp(r *rand.Rand) time.Time { // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. func SimulateFromSeed( - tb testing.TB, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []Operation, setups []RandSetup, + tb testing.TB, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool, ) (simError error) { // in case we have to end early, don't os.Exit so that we can run cleanup code. @@ -171,12 +171,27 @@ func SimulateFromSeed( // 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 []Operation, operationQueue map[int][]Operation, totalNumBlocks int, displayLogs func()) func( +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, totalNumBlocks int, displayLogs func()) func( blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + totalOpWeight := 0 + for i := 0; i < len(ops); i++ { + totalOpWeight += ops[i].Weight + } + selectOp := func(r *rand.Rand) Operation { + x := r.Intn(totalOpWeight) + for i := 0; i < len(ops); i++ { + if x <= ops[i].Weight { + return ops[i].Op + } + x -= ops[i].Weight + } + // shouldn't happen + return ops[0].Op + } return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { for j := 0; j < blocksize; j++ { - logUpdate, futureOps, err := ops[r.Intn(len(ops))](r, app, ctx, keys, event) + logUpdate, futureOps, err := selectOp(r)(r, app, ctx, keys, event) if err != nil { displayLogs() tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err) diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index 25fb1a6e8..401899efe 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -47,6 +47,13 @@ type ( BlockHeight int Op Operation } + + // WeightedOperation is an operation with associated weight. + // This is used to bias the selection operation within the simulator. + WeightedOperation struct { + Weight int + Op Operation + } ) // PeriodicInvariant returns an Invariant function closure that asserts diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 6ad8b4182..764463604 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -44,14 +44,14 @@ func TestStakeWithRandomMessages(t *testing.T) { simulation.Simulate( t, mapp.BaseApp, appStateFn, - []simulation.Operation{ - SimulateMsgCreateValidator(mapper, stakeKeeper), - SimulateMsgEditValidator(stakeKeeper), - SimulateMsgDelegate(mapper, stakeKeeper), - SimulateMsgBeginUnbonding(mapper, stakeKeeper), - SimulateMsgCompleteUnbonding(stakeKeeper), - SimulateMsgBeginRedelegate(mapper, stakeKeeper), - SimulateMsgCompleteRedelegate(stakeKeeper), + []simulation.WeightedOperation{ + {10, SimulateMsgCreateValidator(mapper, stakeKeeper)}, + {5, SimulateMsgEditValidator(stakeKeeper)}, + {15, SimulateMsgDelegate(mapper, stakeKeeper)}, + {10, SimulateMsgBeginUnbonding(mapper, stakeKeeper)}, + {3, SimulateMsgCompleteUnbonding(stakeKeeper)}, + {10, SimulateMsgBeginRedelegate(mapper, stakeKeeper)}, + {3, SimulateMsgCompleteRedelegate(stakeKeeper)}, }, []simulation.RandSetup{ Setup(mapp, stakeKeeper), }, []simulation.Invariant{ From f1ac53b4ad1d08b3d0acd245e581ecd2d9864c55 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 12 Sep 2018 03:39:34 -0400 Subject: [PATCH 10/13] Merge PR #2270: docs: lotion update --- docs/config.js | 4 ++- docs/lotion/building-an-app.md | 46 ----------------------------- docs/lotion/overview.md | 53 ++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 49 deletions(-) delete mode 100644 docs/lotion/building-an-app.md diff --git a/docs/config.js b/docs/config.js index 93426bcc5..5de166179 100644 --- a/docs/config.js +++ b/docs/config.js @@ -57,7 +57,9 @@ module.exports = { { title: "Lotion JS", collapsable: false, - children: [["/lotion/overview", "Overview"], "/lotion/building-an-app"] + children: [ + ["/lotion/overview", "Overview"] + ] }, { title: "Validators", diff --git a/docs/lotion/building-an-app.md b/docs/lotion/building-an-app.md deleted file mode 100644 index 93b3363c5..000000000 --- a/docs/lotion/building-an-app.md +++ /dev/null @@ -1,46 +0,0 @@ -# Building an App - -::: tip -Lotion requires __node v7.6.0__ or higher, and a mac or linux machine. -::: - -## Installation -``` -$ npm install lotion -``` - -## Simple App -`app.js`: -```js -let lotion = require('lotion') - -let app = lotion({ - initialState: { - count: 0 - } -}) - -app.use(function (state, tx) { - if(state.count === tx.nonce) { - state.count++ - } -}) - -app.listen(3000) -``` - -run `node app.js`, then: -```bash -$ curl http://localhost:3000/state -# { "count": 0 } - -$ curl http://localhost:3000/txs -d '{ "nonce": 0 }' -# { "ok": true } - -$ curl http://localhost:3000/state -# { "count": 1 } -``` - -## Learn More - -You can learn more about Lotion JS by visiting Lotion on [Github](https://github.com/keppel/lotion). diff --git a/docs/lotion/overview.md b/docs/lotion/overview.md index 03c79c571..b28d2c75b 100644 --- a/docs/lotion/overview.md +++ b/docs/lotion/overview.md @@ -1,5 +1,54 @@ -# Lotion JS Overview +# Overview -Lotion is a new way to create blockchain apps in JavaScript, which aims to make writing new blockchains fast and fun. It builds on top of Tendermint using the ABCI protocol. Lotion lets you write secure, scalable applications that can easily interoperate with other blockchains on the Cosmos Network using IBC. +Lotion is an alternative to the Cosmos SDK and allows you to create blockchain apps in JavaScript. It aims to make writing new blockchain apps fast and easy by using the ABCI protocol to build on top of Tendermint. Lotion lets you write secure, scalable applications that can easily interoperate with other blockchains on the Cosmos Network using IBC. Lotion itself is a tiny framework; its true power comes from the network of small, focused modules built upon it. Adding a fully-featured cryptocurrency to your blockchain, for example, takes only a few lines of code. + +For more information see the [website](https://lotionjs.com) and [GitHub repo](https://github.com/keppel/lotion), for complete documentation which expands on the following example. + +## Building an App + +### Installation + +::: tip +Lotion requires __node v7.6.0__ or higher, and a mac or linux machine. +::: + +``` +$ npm install lotion +``` + +### Simple App + +`app.js`: + +```js +let lotion = require('lotion') + +let app = lotion({ + initialState: { + count: 0 + } +}) + +app.use(function (state, tx) { + if(state.count === tx.nonce) { + state.count++ + } +}) + +app.listen(3000) +``` + +run `node app.js`, then: + +```bash +$ curl http://localhost:3000/state +# { "count": 0 } + +$ curl http://localhost:3000/txs -d '{ "nonce": 0 }' +# { "ok": true } + +$ curl http://localhost:3000/state +# { "count": 1 } +``` From 5834a500dae7c9de8df41f72a38c322dedaf3d54 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 12 Sep 2018 09:41:09 +0200 Subject: [PATCH 11/13] Merge PR #2259: Minor changes on slashing logs and gov Querier --- x/gov/handler.go | 14 ++++++++++---- x/gov/queryable.go | 35 +++++++++++++++++++++++------------ x/stake/keeper/slash.go | 24 ++++++++++++++++-------- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/x/gov/handler.go b/x/gov/handler.go index 6424bb0a1..5e4557155 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -114,8 +114,14 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags.AppendTag(tags.Action, tags.ActionProposalDropped) resTags.AppendTag(tags.ProposalID, proposalIDBytes) - logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - didn't mean minimum deposit (had only %s), deleted", - inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), inactiveProposal.GetTotalDeposit())) + logger.Info( + fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %s steak); deleted", + inactiveProposal.GetProposalID(), + inactiveProposal.GetTitle(), + keeper.GetDepositProcedure(ctx).MinDeposit.AmountOf("steak"), + inactiveProposal.GetTotalDeposit().AmountOf("steak"), + ), + ) } // Check if earliest Active Proposal ended voting period yet @@ -143,7 +149,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { activeProposal.SetTallyResult(tallyResults) keeper.SetProposal(ctx, activeProposal) - logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - tallied, passed: %v", + logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) for _, valAddr := range nonVotingVals { @@ -154,7 +160,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { val.GetPower().RoundInt64(), keeper.GetTallyingProcedure(ctx).GovernancePenalty) - logger.Info(fmt.Sprintf("Validator %s failed to vote on proposal %d, slashing", + logger.Info(fmt.Sprintf("validator %s failed to vote on proposal %d; slashing", val.GetOperator(), activeProposal.GetProposalID())) } diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 090b9a914..6aa0c9884 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -8,22 +8,33 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +// query endpoints supported by the governance Querier +const ( + QueryProposals = "proposals" + QueryProposal = "proposal" + QueryDeposits = "deposits" + QueryDeposit = "deposit" + QueryVotes = "votes" + QueryVote = "vote" + QueryTally = "tally" +) + func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - case "proposal": - return queryProposal(ctx, path[1:], req, keeper) - case "deposit": - return queryDeposit(ctx, path[1:], req, keeper) - case "vote": - return queryVote(ctx, path[1:], req, keeper) - case "deposits": - return queryDeposits(ctx, path[1:], req, keeper) - case "votes": - return queryVotes(ctx, path[1:], req, keeper) - case "proposals": + case QueryProposals: return queryProposals(ctx, path[1:], req, keeper) - case "tally": + case QueryProposal: + return queryProposal(ctx, path[1:], req, keeper) + case QueryDeposits: + return queryDeposits(ctx, path[1:], req, keeper) + case QueryDeposit: + return queryDeposit(ctx, path[1:], req, keeper) + case QueryVotes: + return queryVotes(ctx, path[1:], req, keeper) + case QueryVote: + return queryVote(ctx, path[1:], req, keeper) + case QueryTally: return queryTally(ctx, path[1:], req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown gov query endpoint") diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 5a004e807..52dbd21c7 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -28,7 +28,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in logger := ctx.Logger().With("module", "x/stake") if slashFactor.LT(sdk.ZeroDec()) { - panic(fmt.Errorf("attempted to slash with a negative slashFactor: %v", slashFactor)) + panic(fmt.Errorf("attempted to slash with a negative slash factor: %v", slashFactor)) } // Amount of slashing = slash slashFactor * power at time of infraction @@ -50,7 +50,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // should not be slashing unbonded if validator.IsUnbonded(ctx) { - panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator)) + panic(fmt.Sprintf("should not be slashing unbonded validator: %s", validator.GetOperator())) } operatorAddress := validator.GetOperator() @@ -72,7 +72,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations logger.Info(fmt.Sprintf( - "Slashing at current height %d, not scanning unbonding delegations & redelegations", + "slashing at current height %d, not scanning unbonding delegations & redelegations", infractionHeight)) case infractionHeight < ctx.BlockHeight(): @@ -117,8 +117,8 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // Log that a slash occurred! logger.Info(fmt.Sprintf( - "Validator %s slashed by slashFactor %s, burned %v tokens", - pubkey.Address(), slashFactor.String(), tokensToBurn)) + "validator %s slashed by slash factor of %s; burned %v tokens", + validator.GetOperator(), slashFactor.String(), tokensToBurn)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return @@ -127,8 +127,12 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // jail a validator func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) { k.setJailed(ctx, pubkey, true) + validatorAddr, err := sdk.ValAddressFromHex(pubkey.Address().String()) + if err != nil { + panic(err.Error()) + } logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s jailed", pubkey.Address())) + logger.Info(fmt.Sprintf("validator %s jailed", validatorAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } @@ -136,8 +140,12 @@ func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) { // unjail a validator func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { k.setJailed(ctx, pubkey, false) + validatorAddr, err := sdk.ValAddressFromHex(pubkey.Address().String()) + if err != nil { + panic(err.Error()) + } logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s unjailed", pubkey.Address())) + logger.Info(fmt.Sprintf("validator %s unjailed", validatorAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } @@ -146,7 +154,7 @@ func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) { validator, found := k.GetValidatorByPubKey(ctx, pubkey) if !found { - panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed)) + panic(fmt.Errorf("validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed)) } validator.Jailed = isJailed k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it From 8682556fad5ab71defd16c6e983d16150f42a731 Mon Sep 17 00:00:00 2001 From: philipstanislaus Date: Wed, 12 Sep 2018 10:02:03 +0200 Subject: [PATCH 12/13] Merge PR #1993: Add optional flag to `gaiad testnet` to make config directory of daemon and cli configurable --- PENDING.md | 2 +- server/testnet.go | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/PENDING.md b/PENDING.md index d1d25c827..b313f5cf6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -47,7 +47,6 @@ BREAKING CHANGES * Tendermint - FEATURES * Gaia REST API (`gaiacli advanced rest-server`) @@ -111,6 +110,7 @@ IMPROVEMENTS * [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282 * [simulation] Add a concept of weighting the operations \#2303 * [simulation] Logs get written to file if large, and also get printed on panics \#2285 + * [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable * Tendermint diff --git a/server/testnet.go b/server/testnet.go index 93f563005..951e378d1 100644 --- a/server/testnet.go +++ b/server/testnet.go @@ -18,9 +18,11 @@ import ( ) var ( - nodeDirPrefix = "node-dir-prefix" - nValidators = "v" - outputDir = "output-dir" + nodeDirPrefix = "node-dir-prefix" + nValidators = "v" + outputDir = "output-dir" + nodeDaemonHome = "node-daemon-home" + nodeCliHome = "node-cli-home" startingIPAddress = "starting-ip-address" ) @@ -39,7 +41,7 @@ Note, strict routability for addresses is turned off in the config file. Example: - gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 + gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2 `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config @@ -53,6 +55,10 @@ Example: "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", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") @@ -66,8 +72,10 @@ func testnetWithConfig(config *cfg.Config, cdc *wire.Codec, appInit AppInit) err // Generate private key, node ID, initial transaction for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) - nodeDir := filepath.Join(outDir, nodeDirName, "gaiad") - clientDir := filepath.Join(outDir, nodeDirName, "gaiacli") + nodeDaemonHomeName := viper.GetString(nodeDaemonHome) + nodeCliHomeName := viper.GetString(nodeCliHome) + nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) + clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName) gentxsDir := filepath.Join(outDir, "gentxs") config.SetRoot(nodeDir) @@ -122,7 +130,8 @@ func testnetWithConfig(config *cfg.Config, cdc *wire.Codec, appInit AppInit) err for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) - nodeDir := filepath.Join(outDir, nodeDirName, "gaiad") + nodeDaemonHomeName := viper.GetString(nodeDaemonHome) + nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) gentxsDir := filepath.Join(outDir, "gentxs") initConfig := InitConfig{ chainID, From 5bf9401e871af6f7ba715b54a86044e93de8fe7e Mon Sep 17 00:00:00 2001 From: Rigel Date: Thu, 13 Sep 2018 00:53:55 -0400 Subject: [PATCH 13/13] Merge PR #2310: staking transient store for Tendermint Updates * working * non-tests compile * fix mounting error, working on testing * stumped using transient store * joon comments * remove old comments * resolve ibc error * lint/sim_test * fix determinism sim test * sim enable commit * docs and pending --- Makefile | 4 +- PENDING.md | 1 + baseapp/baseapp.go | 9 ++- cmd/gaia/app/app.go | 9 ++- cmd/gaia/app/sim_test.go | 4 +- cmd/gaia/cmd/gaiadebug/hack.go | 4 +- examples/democoin/x/cool/app_test.go | 2 +- examples/democoin/x/pow/app_test.go | 2 +- x/bank/bench_test.go | 2 +- x/bank/simulation/sim_test.go | 2 +- x/gov/handler.go | 2 +- x/gov/simulation/sim_test.go | 5 +- x/gov/test_common.go | 5 +- x/ibc/app_test.go | 2 +- x/mock/app.go | 15 +++- x/mock/app_test.go | 2 +- x/mock/simulation/random_simulate_blocks.go | 18 ++--- x/slashing/app_test.go | 5 +- x/slashing/test_common.go | 4 +- x/stake/app_test.go | 5 +- x/stake/handler.go | 1 - x/stake/keeper/_store.md | 10 ++- x/stake/keeper/keeper.go | 4 +- x/stake/keeper/key.go | 22 +++--- x/stake/keeper/test_common.go | 4 +- x/stake/keeper/validator.go | 31 +++----- x/stake/keeper/validator_test.go | 80 +++++++-------------- x/stake/simulation/sim_test.go | 5 +- x/stake/stake.go | 4 +- 29 files changed, 134 insertions(+), 129 deletions(-) diff --git a/Makefile b/Makefile index ee76c6d19..a015e5a6b 100644 --- a/Makefile +++ b/Makefile @@ -157,11 +157,11 @@ 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 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h test_sim_gaia_slow: @echo "Running full Gaia simulation. This may take awhile!" - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -SimulationCommit=true -v -timeout 24h SIM_NUM_BLOCKS ?= 210 SIM_BLOCK_SIZE ?= 200 diff --git a/PENDING.md b/PENDING.md index b313f5cf6..33b60198c 100644 --- a/PENDING.md +++ b/PENDING.md @@ -33,6 +33,7 @@ BREAKING CHANGES renamed for accounts and validator operators: * `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub` * `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub` + * [x/stake] [#1013] TendermintUpdates now uses transient store * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 5552ff784..10d9f55f7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -120,13 +120,20 @@ func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.Codespace return app.codespacer.RegisterNext(codespace) } -// Mount a store to the provided key in the BaseApp multistore +// Mount IAVL stores to the provided keys in the BaseApp multistore func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) { for _, key := range keys { app.MountStore(key, sdk.StoreTypeIAVL) } } +// Mount stores to the provided keys in the BaseApp multistore +func (app *BaseApp) MountStoresTransient(keys ...*sdk.TransientStoreKey) { + for _, key := range keys { + app.MountStore(key, sdk.StoreTypeTransient) + } +} + // Mount a store to the provided key in the BaseApp multistore, using a specified DB func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) { app.cms.MountStoreWithDB(key, typ, db) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index c80da0bd0..7cf235b16 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -43,6 +43,7 @@ type GaiaApp struct { keyAccount *sdk.KVStoreKey keyIBC *sdk.KVStoreKey keyStake *sdk.KVStoreKey + tkeyStake *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey keyGov *sdk.KVStoreKey keyFeeCollection *sdk.KVStoreKey @@ -74,6 +75,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio keyAccount: sdk.NewKVStoreKey("acc"), keyIBC: sdk.NewKVStoreKey("ibc"), keyStake: sdk.NewKVStoreKey("stake"), + tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keySlashing: sdk.NewKVStoreKey("slashing"), keyGov: sdk.NewKVStoreKey("gov"), keyFeeCollection: sdk.NewKVStoreKey("fee"), @@ -92,7 +94,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) app.stakeKeeper = app.stakeKeeper.WithValidatorHooks(app.slashingKeeper.ValidatorHooks()) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) @@ -114,8 +116,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams) - app.MountStore(app.tkeyParams, sdk.StoreTypeTransient) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, + app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams) + app.MountStoresTransient(app.tkeyParams, app.tkeyStake) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index b12f3978c..078eece11 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -209,9 +209,9 @@ func TestAppStateDeterminism(t *testing.T) { []simulation.Invariant{}, 50, 100, - false, + true, ) - app.Commit() + //app.Commit() appHash := app.LastCommitID().Hash appHashList[j] = appHash } diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index b2f1183b3..d0a0ad9d7 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -134,6 +134,7 @@ type GaiaApp struct { keyAccount *sdk.KVStoreKey keyIBC *sdk.KVStoreKey keyStake *sdk.KVStoreKey + tkeyStake *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey keyParams *sdk.KVStoreKey @@ -161,6 +162,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp keyAccount: sdk.NewKVStoreKey("acc"), keyIBC: sdk.NewKVStoreKey("ibc"), keyStake: sdk.NewKVStoreKey("stake"), + tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keySlashing: sdk.NewKVStoreKey("slashing"), keyParams: sdk.NewKVStoreKey("params"), } @@ -176,7 +178,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes diff --git a/examples/democoin/x/cool/app_test.go b/examples/democoin/x/cool/app_test.go index 8d778045e..3349928eb 100644 --- a/examples/democoin/x/cool/app_test.go +++ b/examples/democoin/x/cool/app_test.go @@ -55,7 +55,7 @@ func getMockApp(t *testing.T) *mock.App { mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold")) - require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyCool})) + require.NoError(t, mapp.CompleteSetup(keyCool)) return mapp } diff --git a/examples/democoin/x/pow/app_test.go b/examples/democoin/x/pow/app_test.go index de8642e4c..0a4f95cf1 100644 --- a/examples/democoin/x/pow/app_test.go +++ b/examples/democoin/x/pow/app_test.go @@ -32,7 +32,7 @@ func getMockApp(t *testing.T) *mock.App { mapp.SetInitChainer(getInitChainer(mapp, keeper)) - require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyPOW})) + require.NoError(t, mapp.CompleteSetup(keyPOW)) mapp.Seal() diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index 998d4e4bc..2a3cd5e66 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -19,7 +19,7 @@ func getBenchmarkMockApp() (*mock.App, error) { bankKeeper := NewBaseKeeper(mapp.AccountMapper) mapp.Router().AddRoute("bank", NewHandler(bankKeeper)) - err := mapp.CompleteSetup([]*sdk.KVStoreKey{}) + err := mapp.CompleteSetup() return mapp, err } diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index 555dc9a6c..cd2353c6e 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -21,7 +21,7 @@ func TestBankWithRandomMessages(t *testing.T) { bankKeeper := bank.NewBaseKeeper(mapper) mapp.Router().AddRoute("bank", bank.NewHandler(bankKeeper)) - err := mapp.CompleteSetup([]*sdk.KVStoreKey{}) + err := mapp.CompleteSetup() if err != nil { panic(err) } diff --git a/x/gov/handler.go b/x/gov/handler.go index 5e4557155..c99674926 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -115,7 +115,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags.AppendTag(tags.ProposalID, proposalIDBytes) logger.Info( - fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %s steak); deleted", + fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %v steak); deleted", inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), keeper.GetDepositProcedure(ctx).MinDeposit.AmountOf("steak"), diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index 411dbf0c8..841b4c0f7 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -26,7 +26,8 @@ func TestGovWithRandomMessages(t *testing.T) { mapper := mapp.AccountMapper bankKeeper := bank.NewBaseKeeper(mapper) stakeKey := sdk.NewKVStoreKey("stake") - stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, bankKeeper, stake.DefaultCodespace) + stakeTKey := sdk.NewTransientStoreKey("transient_stake") + stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace) paramKey := sdk.NewKVStoreKey("params") paramKeeper := params.NewKeeper(mapp.Cdc, paramKey) govKey := sdk.NewKVStoreKey("gov") @@ -37,7 +38,7 @@ func TestGovWithRandomMessages(t *testing.T) { return abci.ResponseEndBlock{} }) - err := mapp.CompleteSetup([]*sdk.KVStoreKey{stakeKey, paramKey, govKey}) + err := mapp.CompleteSetup(stakeKey, stakeTKey, paramKey, govKey) if err != nil { panic(err) } diff --git a/x/gov/test_common.go b/x/gov/test_common.go index a0d556521..f2f3625e6 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -28,18 +28,19 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, keyGlobalParams := sdk.NewKVStoreKey("params") keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") keyGov := sdk.NewKVStoreKey("gov") pk := params.NewKeeper(mapp.Cdc, keyGlobalParams) ck := bank.NewBaseKeeper(mapp.AccountMapper) - sk := stake.NewKeeper(mapp.Cdc, keyStake, ck, mapp.RegisterCodespace(stake.DefaultCodespace)) + sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, mapp.RegisterCodespace(stake.DefaultCodespace)) keeper := NewKeeper(mapp.Cdc, keyGov, pk.Setter(), ck, sk, DefaultCodespace) mapp.Router().AddRoute("gov", NewHandler(keeper)) mapp.SetEndBlocker(getEndBlocker(keeper)) mapp.SetInitChainer(getInitChainer(mapp, keeper, sk)) - require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keyGov, keyGlobalParams})) + require.NoError(t, mapp.CompleteSetup(keyStake, keyGov, keyGlobalParams, tkeyStake)) genAccs, addrs, pubKeys, privKeys := mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin("steak", 42)}) diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go index 07c3616ce..e92e71a8c 100644 --- a/x/ibc/app_test.go +++ b/x/ibc/app_test.go @@ -24,7 +24,7 @@ func getMockApp(t *testing.T) *mock.App { bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper) mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, bankKeeper)) - require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyIBC})) + require.NoError(t, mapp.CompleteSetup(keyIBC)) return mapp } diff --git a/x/mock/app.go b/x/mock/app.go index 97068a3a5..ebfb97ae5 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -1,6 +1,7 @@ package mock import ( + "fmt" "math/rand" "os" @@ -75,11 +76,21 @@ func NewApp() *App { // CompleteSetup completes the application setup after the routes have been // registered. -func (app *App) CompleteSetup(newKeys []*sdk.KVStoreKey) error { +func (app *App) CompleteSetup(newKeys ...sdk.StoreKey) error { newKeys = append(newKeys, app.KeyMain) newKeys = append(newKeys, app.KeyAccount) - app.MountStoresIAVL(newKeys...) + for _, key := range newKeys { + switch key.(type) { + case *sdk.KVStoreKey: + app.MountStore(key, sdk.StoreTypeIAVL) + case *sdk.TransientStoreKey: + app.MountStore(key, sdk.StoreTypeTransient) + default: + return fmt.Errorf("unsupported StoreKey: %+v", key) + } + } + err := app.LoadLatestVersion(app.KeyMain) return err diff --git a/x/mock/app_test.go b/x/mock/app_test.go index 460757a04..1319482ca 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -41,7 +41,7 @@ func getMockApp(t *testing.T) *App { mApp := NewApp() mApp.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return }) - require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{})) + require.NoError(t, mApp.CompleteSetup()) return mApp } diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index c5bbd2ac1..de78ae094 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -24,10 +24,11 @@ import ( ) // Simulate tests application by sending random messages. -func Simulate( - t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []WeightedOperation, setups []RandSetup, - invariants []Invariant, numBlocks int, blockSize int, commit bool, -) error { +func Simulate(t *testing.T, app *baseapp.BaseApp, + appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + ops []WeightedOperation, setups []RandSetup, + invariants []Invariant, numBlocks int, blockSize int, commit bool) error { + time := time.Now().UnixNano() return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } @@ -54,10 +55,11 @@ func randTimestamp(r *rand.Rand) time.Time { // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. -func SimulateFromSeed( - tb testing.TB, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []WeightedOperation, setups []RandSetup, - invariants []Invariant, numBlocks int, blockSize int, commit bool, -) (simError error) { +func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, + appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, + numBlocks int, blockSize int, commit bool) (simError error) { + // in case we have to end early, don't os.Exit so that we can run cleanup code. stopEarly := false testingMode, t, b := getTestingMode(tb) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 4ceeac94d..90e8137e2 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -26,11 +26,12 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) { RegisterWire(mapp.Cdc) keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") keySlashing := sdk.NewKVStoreKey("slashing") keyParams := sdk.NewKVStoreKey("params") bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper) paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams) - stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, bankKeeper, mapp.RegisterCodespace(stake.DefaultCodespace)) + stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, mapp.RegisterCodespace(stake.DefaultCodespace)) keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Getter(), mapp.RegisterCodespace(DefaultCodespace)) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) @@ -38,7 +39,7 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) { mapp.SetEndBlocker(getEndBlocker(stakeKeeper)) mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper)) - require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keySlashing, keyParams})) + require.NoError(t, mapp.CompleteSetup(keyStake, keySlashing, keyParams, tkeyStake)) return mapp, stakeKeeper, keeper } diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index b10c640f5..a21930ad9 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -52,11 +52,13 @@ func createTestCodec() *wire.Codec { func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, params.Setter, Keeper) { keyAcc := sdk.NewKVStoreKey("acc") keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") keySlashing := sdk.NewKVStoreKey("slashing") keyParams := sdk.NewKVStoreKey("params") db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil) ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) @@ -67,7 +69,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount) ck := bank.NewBaseKeeper(accountMapper) params := params.NewKeeper(cdc, keyParams) - sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace) + sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, stake.DefaultCodespace) genesis := stake.DefaultGenesisState() genesis.Pool.LooseTokens = sdk.NewDec(initCoins.MulRaw(int64(len(addrs))).Int64()) diff --git a/x/stake/app_test.go b/x/stake/app_test.go index b5cf90433..a787e5e4d 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -34,14 +34,15 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) { RegisterWire(mApp.Cdc) keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") bankKeeper := bank.NewBaseKeeper(mApp.AccountMapper) - keeper := NewKeeper(mApp.Cdc, keyStake, bankKeeper, mApp.RegisterCodespace(DefaultCodespace)) + keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, bankKeeper, mApp.RegisterCodespace(DefaultCodespace)) mApp.Router().AddRoute("stake", NewHandler(keeper)) mApp.SetEndBlocker(getEndBlocker(keeper)) mApp.SetInitChainer(getInitChainer(mApp, keeper)) - require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{keyStake})) + require.NoError(t, mApp.CompleteSetup(keyStake, tkeyStake)) return mApp, keeper } diff --git a/x/stake/handler.go b/x/stake/handler.go index b00db571d..e7641393d 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -53,7 +53,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid // calculate validator set changes ValidatorUpdates = k.GetValidTendermintUpdates(ctx) - k.ClearTendermintUpdates(ctx) return } diff --git a/x/stake/keeper/_store.md b/x/stake/keeper/_store.md index 818b17ac0..5c070b9e0 100644 --- a/x/stake/keeper/_store.md +++ b/x/stake/keeper/_store.md @@ -3,6 +3,7 @@ This document provided a bit more insight as to the purpose of several related prefixed areas of the staking store which are accessed in `x/stake/keeper.go`. +# IAVL Store ## Validators - Prefix Key Space: ValidatorsKey @@ -36,10 +37,13 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`. through this set to determine who we've kicked out. retrieving validator by tendermint index +# Transient Store + +The transient store persists between transations but not between blocks + ## Tendermint Updates - - Prefix Key Space: TendermintUpdatesKey + - Prefix Key Space: TendermintUpdatesTKey - Key/Sort: Validator Operator Address - Value: Tendermint ABCI Validator - Contains: Validators are queued to affect the consensus validation set in Tendermint - - Used For: Informing Tendermint of the validator set updates, is used only intra-block, as the - updates are applied then cleared on endblock + - Used For: Informing Tendermint of the validator set updates diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index c928c9744..cbf6e39e4 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -11,6 +11,7 @@ import ( // keeper of the stake store type Keeper struct { storeKey sdk.StoreKey + storeTKey sdk.StoreKey cdc *wire.Codec bankKeeper bank.Keeper validatorHooks sdk.ValidatorHooks @@ -19,9 +20,10 @@ type Keeper struct { codespace sdk.CodespaceType } -func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *wire.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ storeKey: key, + storeTKey: tkey, cdc: cdc, bankKeeper: ck, validatorHooks: nil, diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 9dbf50c03..c445e2552 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -22,14 +22,16 @@ var ( ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff - TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated - IntraTxCounterKey = []byte{0x09} // key for intra-block tx index - DelegationKey = []byte{0x0A} // key for a delegation - UnbondingDelegationKey = []byte{0x0B} // key for an unbonding-delegation - UnbondingDelegationByValIndexKey = []byte{0x0C} // prefix for each key for an unbonding-delegation, by validator operator - RedelegationKey = []byte{0x0D} // key for a redelegation - RedelegationByValSrcIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by source validator operator - RedelegationByValDstIndexKey = []byte{0x0F} // prefix for each key for an redelegation, by destination validator operator + IntraTxCounterKey = []byte{0x08} // key for intra-block tx index + DelegationKey = []byte{0x09} // key for a delegation + UnbondingDelegationKey = []byte{0x0A} // key for an unbonding-delegation + UnbondingDelegationByValIndexKey = []byte{0x0B} // prefix for each key for an unbonding-delegation, by validator operator + RedelegationKey = []byte{0x0C} // key for a redelegation + RedelegationByValSrcIndexKey = []byte{0x0D} // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by destination validator operator + + // Keys for store prefixes (transient) + TendermintUpdatesTKey = []byte{0x00} // prefix for each key to a validator which is being updated ) const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch @@ -98,8 +100,8 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { // get the key for the accumulated update validators // VALUE: abci.Validator // note records using these keys should never persist between blocks -func GetTendermintUpdatesKey(operatorAddr sdk.ValAddress) []byte { - return append(TendermintUpdatesKey, operatorAddr.Bytes()...) +func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte { + return append(TendermintUpdatesTKey, operatorAddr.Bytes()...) } //______________________________________________________________________________ diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 247ff262b..8ebded7bf 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -90,10 +90,12 @@ func ParamsNoInflation() types.Params { func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) { keyStake := sdk.NewKVStoreKey("stake") + tkeyStake := sdk.NewTransientStoreKey("transient_stake") keyAcc := sdk.NewKVStoreKey("acc") db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil) ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) err := ms.LoadLatestVersion() @@ -107,7 +109,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context auth.ProtoBaseAccount, // prototype ) ck := bank.NewBaseKeeper(accountMapper) - keeper := NewKeeper(cdc, keyStake, ck, types.DefaultCodespace) + keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace) keeper.SetPool(ctx, types.InitialPool()) keeper.SetNewParams(ctx, types.DefaultParams()) keeper.InitIntraTxCounter(ctx) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index aaed74be4..1f65aaa95 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -203,9 +203,9 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { // at the previous block height or were removed from the validator set entirely // are returned to Tendermint. func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) { - store := ctx.KVStore(k.storeKey) + tstore := ctx.TransientStore(k.storeTKey) - iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) + iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey) for ; iterator.Valid(); iterator.Next() { var abciVal abci.Validator @@ -233,18 +233,6 @@ func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Valid return } -// remove all validator update entries after applied to Tendermint -func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) - - // delete subspace - iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) - for ; iterator.Valid(); iterator.Next() { - store.Delete(iterator.Key()) - } - iterator.Close() -} - //___________________________________________________________________________ // Perform all the necessary steps for when a validator changes its power. This @@ -255,7 +243,7 @@ func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) { // nolint: gocyclo // TODO: Remove above nolint, function needs to be simplified! func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator { - store := ctx.KVStore(k.storeKey) + tstore := ctx.TransientStore(k.storeTKey) pool := k.GetPool(ctx) oldValidator, oldFound := k.GetValidator(ctx, validator.OperatorAddr) @@ -280,7 +268,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type (oldFound && oldValidator.Status == sdk.Bonded): bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) - store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bz) + tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) if cliffValExists { cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx)) @@ -315,7 +303,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type // if decreased in power but still bonded, update Tendermint validator if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) { bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) - store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bz) + tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) } } @@ -645,7 +633,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat // add to accumulated changes for tendermint bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero()) - store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bzABCI) + tstore := ctx.TransientStore(k.storeTKey) + tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) // also remove from the Bonded types.Validators Store store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) @@ -682,7 +671,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. // add to accumulated changes for tendermint bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator()) - store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bzABCI) + tstore := ctx.TransientStore(k.storeTKey) + tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) // call the bond hook if present if k.validatorHooks != nil { @@ -717,7 +707,8 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero()) - store.Set(GetTendermintUpdatesKey(address), bz) + tstore := ctx.TransientStore(k.storeTKey) + tstore.Set(GetTendermintUpdatesTKey(address), bz) } //__________________________________________________________________________ diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index d9531ae1a..89dd40677 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -6,12 +6,25 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// for testing, remove all validator update entries after applied to Tendermint +func clearTendermintUpdates(ctx sdk.Context, k Keeper) { + store := ctx.TransientStore(k.storeTKey) + + // delete subspace + iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesTKey) + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } + iterator.Close() +} + +//_______________________________________________________ + func TestSetValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 10) pool := keeper.GetPool(ctx) @@ -636,32 +649,6 @@ func TestFullValidatorSetPowerChange(t *testing.T) { assert.True(ValEq(t, validators[2], resValidators[1])) } -// clear the tracked changes to the gotValidator set -func TestClearTendermintUpdates(t *testing.T) { - ctx, _, keeper := CreateTestInput(t, false, 1000) - - amts := []int64{100, 400, 200} - validators := make([]types.Validator, len(amts)) - for i, amt := range amts { - pool := keeper.GetPool(ctx) - - valPubKey := PKs[i] - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - - validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) - - keeper.SetPool(ctx, pool) - keeper.UpdateValidator(ctx, validators[i]) - } - - updates := keeper.GetValidTendermintUpdates(ctx) - require.Equal(t, len(amts), len(updates)) - keeper.ClearTendermintUpdates(ctx) - updates = keeper.GetValidTendermintUpdates(ctx) - require.Equal(t, 0, len(updates)) -} - func TestGetValidTendermintUpdatesAllNone(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) @@ -688,21 +675,6 @@ func TestGetValidTendermintUpdatesAllNone(t *testing.T) { assert.Equal(t, 2, len(updates)) assert.Equal(t, validators[0].ABCIValidator(), updates[0]) assert.Equal(t, validators[1].ABCIValidator(), updates[1]) - - // test from something to nothing - // tendermintUpdate set: {} -> {c1, c2, c3, c4} - keeper.ClearTendermintUpdates(ctx) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) - - keeper.RemoveValidator(ctx, validators[0].OperatorAddr) - keeper.RemoveValidator(ctx, validators[1].OperatorAddr) - - updates = keeper.GetValidTendermintUpdates(ctx) - assert.Equal(t, 2, len(updates)) - assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].ConsPubKey), updates[0].PubKey) - assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].ConsPubKey), updates[1].PubKey) - assert.Equal(t, int64(0), updates[0].Power) - assert.Equal(t, int64(0), updates[1].Power) } func TestGetValidTendermintUpdatesIdentical(t *testing.T) { @@ -718,7 +690,7 @@ func TestGetValidTendermintUpdatesIdentical(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test identical, @@ -741,7 +713,7 @@ func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test single value change @@ -769,7 +741,7 @@ func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test multiple value change @@ -800,7 +772,7 @@ func TestGetValidTendermintUpdatesInserted(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test validtor added at the beginning @@ -812,7 +784,7 @@ func TestGetValidTendermintUpdatesInserted(t *testing.T) { // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) validators[3] = keeper.UpdateValidator(ctx, validators[3]) updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) @@ -820,7 +792,7 @@ func TestGetValidTendermintUpdatesInserted(t *testing.T) { // test validtor added at the end // tendermintUpdate set: {} -> {c0} - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) validators[4] = keeper.UpdateValidator(ctx, validators[4]) updates = keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) @@ -843,7 +815,7 @@ func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // test validator added at the end but not inserted in the valset @@ -854,7 +826,7 @@ func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) { // test validator change its power and become a gotValidator (pushing out an existing) // tendermintUpdate set: {} -> {c0, c4} - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) pool := keeper.GetPool(ctx) @@ -881,7 +853,7 @@ func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) { } validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // check initial power @@ -937,7 +909,7 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) { require.Equal(t, validators[0].ABCIValidator(), updates[0]) require.Equal(t, validators[1].ABCIValidator(), updates[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // update initial validator set @@ -1013,7 +985,7 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { require.Equal(t, validators[2].ABCIValidator(), updates[0]) require.Equal(t, validators[1].ABCIValidator(), updates[1]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) // delegate to validator with lowest power but not enough to bond @@ -1054,6 +1026,6 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { require.Equal(t, 1, len(updates)) require.Equal(t, validators[1].ABCIValidator(), updates[0]) - keeper.ClearTendermintUpdates(ctx) + clearTendermintUpdates(ctx, keeper) require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) } diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 764463604..3bfd665f4 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -23,7 +23,8 @@ func TestStakeWithRandomMessages(t *testing.T) { mapper := mapp.AccountMapper bankKeeper := bank.NewBaseKeeper(mapper) stakeKey := sdk.NewKVStoreKey("stake") - stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, bankKeeper, stake.DefaultCodespace) + stakeTKey := sdk.NewTransientStoreKey("transient_stake") + stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { validatorUpdates := stake.EndBlocker(ctx, stakeKeeper) @@ -32,7 +33,7 @@ func TestStakeWithRandomMessages(t *testing.T) { } }) - err := mapp.CompleteSetup([]*sdk.KVStoreKey{stakeKey}) + err := mapp.CompleteSetup(stakeKey, stakeTKey) if err != nil { panic(err) } diff --git a/x/stake/stake.go b/x/stake/stake.go index d60e40299..767169f9c 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -33,7 +33,7 @@ var ( GetValidatorByPubKeyIndexKey = keeper.GetValidatorByPubKeyIndexKey GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey - GetTendermintUpdatesKey = keeper.GetTendermintUpdatesKey + GetTendermintUpdatesTKey = keeper.GetTendermintUpdatesTKey GetDelegationKey = keeper.GetDelegationKey GetDelegationsKey = keeper.GetDelegationsKey ParamKey = keeper.ParamKey @@ -44,7 +44,7 @@ var ( ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey ValidatorPowerCliffKey = keeper.ValidatorPowerCliffKey - TendermintUpdatesKey = keeper.TendermintUpdatesKey + TendermintUpdatesTKey = keeper.TendermintUpdatesTKey DelegationKey = keeper.DelegationKey IntraTxCounterKey = keeper.IntraTxCounterKey GetUBDKey = keeper.GetUBDKey