Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution

This commit is contained in:
rigelrozanski 2018-09-13 02:11:07 -04:00
commit 46db96bdb7
59 changed files with 887 additions and 502 deletions

1
Gopkg.lock generated
View File

@ -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

View File

@ -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=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

View File

@ -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
@ -43,10 +44,10 @@ 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
FEATURES
* Gaia REST API (`gaiacli advanced rest-server`)
@ -61,8 +62,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.
@ -97,6 +99,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.
@ -106,6 +109,9 @@ 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
* [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable
* Tendermint
@ -118,6 +124,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)

View File

@ -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)

View File

@ -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
}

View File

@ -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", "<host>:<port> 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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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())

View File

@ -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)},
}
}
@ -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
@ -204,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
}

View File

@ -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)

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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).

View File

@ -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 }
```

View File

@ -111,7 +111,7 @@ The `--amount` flag accepts the format `--amount=<value|coin_name>`.
::: 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.
:::

View File

@ -12,7 +12,7 @@ the changes cleared
```golang
EndBlock() ValidatorSetChanges
vsc = GetTendermintUpdates()
vsc = GetValidTendermintUpdates()
ClearTendermintUpdates()
return vsc
```

View File

@ -253,7 +253,7 @@ first time.
Accounts are serialized and stored in a Merkle tree under the key
``base/a/<address>``, where ``<address>`` 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 <https://github.com/tendermint/tendermint/tree/master/crypto>`__. The Merkle tree

View File

@ -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
}

View File

@ -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()

View File

@ -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,

View File

@ -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,

View File

@ -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")
}

View File

@ -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) {

View File

@ -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

View File

@ -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),
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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)
}
@ -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{

View File

@ -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) {

View File

@ -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 %v 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()))
}

View File

@ -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")

View File

@ -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

View File

@ -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)
}
@ -56,10 +57,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 +72,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{

View File

@ -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)})

View File

@ -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
}

View File

@ -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())

View File

@ -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

View File

@ -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
}

View File

@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"os/signal"
"runtime/debug"
"sort"
"strings"
"syscall"
@ -23,12 +24,13 @@ 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,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
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()
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,
@ -53,10 +55,13 @@ 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,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
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)
fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed))
r := rand.New(rand.NewSource(seed))
@ -79,12 +84,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 +100,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 +139,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,19 +162,38 @@ 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
// 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)
@ -324,18 +359,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:
@ -350,5 +381,6 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida
}
}
}
return current
}

View File

@ -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

View File

@ -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())
}
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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())

View File

@ -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
}

View File

@ -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) {

View File

@ -52,8 +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)
k.ClearTendermintUpdates(ctx)
ValidatorUpdates = k.GetValidTendermintUpdates(ctx)
return
}

View File

@ -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

View File

@ -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,

View File

@ -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()...)
}
//______________________________________________________________________________

View File

@ -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

View File

@ -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)

View File

@ -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++
@ -201,32 +198,41 @@ 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) {
store := ctx.KVStore(k.storeKey)
//
// 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) {
tstore := ctx.TransientStore(k.storeTKey)
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey)
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
}
// 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
@ -237,15 +243,20 @@ 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)
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)
var valPowerLTcliffPower bool
if cliffValExists {
valPowerLTcliffPower = (bytes.Compare(valPower, cliffPower) == -1)
}
switch {
@ -257,9 +268,9 @@ 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 cliffPower != nil {
if cliffValExists {
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
if bytes.Equal(cliffAddr, validator.OperatorAddr) {
k.updateCliffValidator(ctx, validator)
@ -267,14 +278,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:
@ -293,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)
}
}
@ -315,7 +325,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
@ -327,12 +337,10 @@ 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))
panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr))
}
newCliffVal = currVal
@ -345,13 +353,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)
@ -382,18 +387,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
}
@ -454,29 +461,25 @@ 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
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
}
@ -502,9 +505,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) {
@ -512,7 +513,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)
@ -560,9 +560,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 +603,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)
}
}
@ -637,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))
@ -662,6 +659,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)
@ -672,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 {
@ -707,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)
}
//__________________________________________________________________________
@ -738,3 +739,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))
}
}

View File

@ -6,18 +6,34 @@ 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)
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 +42,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 +61,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])
}
@ -633,67 +649,35 @@ 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)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], 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)
require.Equal(t, len(amts), len(updates))
keeper.ClearTendermintUpdates(ctx)
updates = keeper.GetTendermintUpdates(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])
// test from something to nothing
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
keeper.RemoveValidator(ctx, validators[0].OperatorAddr)
keeper.RemoveValidator(ctx, validators[1].OperatorAddr)
updates = keeper.GetTendermintUpdates(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 TestGetTendermintUpdatesIdentical(t *testing.T) {
func TestGetValidTendermintUpdatesIdentical(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -706,17 +690,17 @@ 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)))
clearTendermintUpdates(ctx, keeper)
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}
@ -729,8 +713,8 @@ 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)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
@ -738,13 +722,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}
@ -757,8 +741,8 @@ 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)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
@ -769,13 +753,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}
@ -788,34 +772,34 @@ 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)))
clearTendermintUpdates(ctx, keeper)
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])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
clearTendermintUpdates(ctx, keeper)
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])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
clearTendermintUpdates(ctx, keeper)
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
@ -831,32 +815,32 @@ 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)))
clearTendermintUpdates(ctx, keeper)
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)))
clearTendermintUpdates(ctx, keeper)
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}
@ -869,8 +853,8 @@ 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)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// check initial power
require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64())
@ -890,8 +874,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])
clearTendermintUpdates(ctx, keeper)
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])
clearTendermintUpdates(ctx, keeper)
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])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
}

View File

@ -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)
}
@ -44,14 +45,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{

View File

@ -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