From 1e26ba2e0e9c1e0457383ff302a97396c227cddb Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Fri, 28 Sep 2018 21:45:54 -0700 Subject: [PATCH 01/15] CLIContext.Logger -> .Output as it isn't a logger (#2420) This changes .Logger to .Output, as it isn't used anywhere except as os.Stdout. --- client/context/broadcast.go | 18 +++++++++--------- client/context/context.go | 15 ++++++++------- client/lcd/root.go | 2 +- examples/democoin/x/cool/client/cli/tx.go | 4 ---- examples/democoin/x/pow/client/cli/tx.go | 2 -- .../x/simplestake/client/cli/commands.go | 5 +---- x/bank/client/cli/broadcast.go | 2 +- x/bank/client/cli/sendtx.go | 3 --- x/gov/client/cli/tx.go | 4 ---- x/ibc/client/cli/ibctx.go | 2 -- x/slashing/client/cli/tx.go | 3 --- x/stake/client/cli/tx.go | 8 -------- 12 files changed, 20 insertions(+), 48 deletions(-) diff --git a/client/context/broadcast.go b/client/context/broadcast.go index 4e295777c..1240ffa02 100644 --- a/client/context/broadcast.go +++ b/client/context/broadcast.go @@ -91,7 +91,7 @@ func (ctx CLIContext) broadcastTxAsync(txBytes []byte) (*ctypes.ResultBroadcastT return res, err } - if ctx.Logger != nil { + if ctx.Output != nil { if ctx.JSON { type toJSON struct { TxHash string @@ -103,10 +103,10 @@ func (ctx CLIContext) broadcastTxAsync(txBytes []byte) (*ctypes.ResultBroadcastT return res, err } - ctx.Logger.Write(bz) - io.WriteString(ctx.Logger, "\n") + ctx.Output.Write(bz) + io.WriteString(ctx.Output, "\n") } else { - io.WriteString(ctx.Logger, fmt.Sprintf("async tx sent (tx hash: %s)\n", res.Hash)) + io.WriteString(ctx.Output, fmt.Sprintf("async tx sent (tx hash: %s)\n", res.Hash)) } } @@ -128,21 +128,21 @@ func (ctx CLIContext) broadcastTxCommit(txBytes []byte) (*ctypes.ResultBroadcast Response abci.ResponseDeliverTx } - if ctx.Logger != nil { + if ctx.Output != nil { resJSON := toJSON{res.Height, res.Hash.String(), res.DeliverTx} bz, err := ctx.Codec.MarshalJSON(resJSON) if err != nil { return res, err } - ctx.Logger.Write(bz) - io.WriteString(ctx.Logger, "\n") + ctx.Output.Write(bz) + io.WriteString(ctx.Output, "\n") } return res, nil } - if ctx.Logger != nil { + if ctx.Output != nil { resStr := fmt.Sprintf("Committed at block %d (tx hash: %s)\n", res.Height, res.Hash.String()) if ctx.PrintResponse { @@ -151,7 +151,7 @@ func (ctx CLIContext) broadcastTxCommit(txBytes []byte) (*ctypes.ResultBroadcast ) } - io.WriteString(ctx.Logger, resStr) + io.WriteString(ctx.Output, resStr) } return res, nil diff --git a/client/context/context.go b/client/context/context.go index 31cfab705..f93240d95 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -11,14 +11,14 @@ import ( "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client/keys" + cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/libs/cli" tmlite "github.com/tendermint/tendermint/lite" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" "os" - "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/client/keys" - cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" ) const ctxAccStoreName = "acc" @@ -29,7 +29,7 @@ type CLIContext struct { Codec *codec.Codec AccDecoder auth.AccountDecoder Client rpcclient.Client - Logger io.Writer + Output io.Writer Height int64 NodeURI string From string @@ -61,6 +61,7 @@ func NewCLIContext() CLIContext { return CLIContext{ Client: rpc, + Output: os.Stdout, NodeURI: nodeURI, AccountStore: ctxAccStoreName, From: viper.GetString(client.FlagFrom), @@ -162,9 +163,9 @@ func (ctx CLIContext) WithAccountDecoder(decoder auth.AccountDecoder) CLIContext return ctx } -// WithLogger returns a copy of the context with an updated logger. -func (ctx CLIContext) WithLogger(w io.Writer) CLIContext { - ctx.Logger = w +// WithOutput returns a copy of the context with an updated output writer (e.g. stdout). +func (ctx CLIContext) WithOutput(w io.Writer) CLIContext { + ctx.Output = w return ctx } diff --git a/client/lcd/root.go b/client/lcd/root.go index 84d3e806c..4bb7d9e17 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -136,7 +136,7 @@ func createHandler(cdc *codec.Codec) http.Handler { panic(err) } - cliCtx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout) + cliCtx := context.NewCLIContext().WithCodec(cdc) // TODO: make more functional? aka r = keys.RegisterRoutes(r) r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") diff --git a/examples/democoin/x/cool/client/cli/tx.go b/examples/democoin/x/cool/client/cli/tx.go index db6394e88..c30743f03 100644 --- a/examples/democoin/x/cool/client/cli/tx.go +++ b/examples/democoin/x/cool/client/cli/tx.go @@ -1,8 +1,6 @@ package cli import ( - "os" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/context" @@ -24,7 +22,6 @@ func QuizTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) from, err := cliCtx.GetFromAddress() @@ -49,7 +46,6 @@ func SetTrendTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) from, err := cliCtx.GetFromAddress() diff --git a/examples/democoin/x/pow/client/cli/tx.go b/examples/democoin/x/pow/client/cli/tx.go index 4f5dacb1a..22de7e098 100644 --- a/examples/democoin/x/pow/client/cli/tx.go +++ b/examples/democoin/x/pow/client/cli/tx.go @@ -1,7 +1,6 @@ package cli import ( - "os" "strconv" "github.com/cosmos/cosmos-sdk/client/context" @@ -25,7 +24,6 @@ func MineCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) from, err := cliCtx.GetFromAddress() diff --git a/examples/democoin/x/simplestake/client/cli/commands.go b/examples/democoin/x/simplestake/client/cli/commands.go index 5d2790e70..b5c0730ef 100644 --- a/examples/democoin/x/simplestake/client/cli/commands.go +++ b/examples/democoin/x/simplestake/client/cli/commands.go @@ -3,7 +3,6 @@ package cli import ( "encoding/hex" "fmt" - "os" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -33,7 +32,6 @@ func BondTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) from, err := cliCtx.GetFromAddress() @@ -86,8 +84,7 @@ func UnbondTxCmd(cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithLogger(os.Stdout) + WithCodec(cdc) from, err := cliCtx.GetFromAddress() if err != nil { diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index bf3d52943..e7e6bc16d 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -20,7 +20,7 @@ Read a transaction from and broadcast it to a node. If you supply a dash in place of an input filename, the command reads from standard input.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { - cliCtx := context.NewCLIContext().WithCodec(codec).WithLogger(os.Stdout) + cliCtx := context.NewCLIContext().WithCodec(codec) stdTx, err := readAndUnmarshalStdTx(cliCtx.Codec, args[0]) if err != nil { return diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index befe6bf00..573e4d99b 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -1,8 +1,6 @@ package cli import ( - "os" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" @@ -30,7 +28,6 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) if err := cliCtx.EnsureAccountExists(); err != nil { diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 2a759c218..a7b0b4c5b 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -2,7 +2,6 @@ package cli import ( "fmt" - "os" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -80,7 +79,6 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) fromAddr, err := cliCtx.GetFromAddress() @@ -164,7 +162,6 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) depositerAddr, err := cliCtx.GetFromAddress() @@ -210,7 +207,6 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) voterAddr, err := cliCtx.GetFromAddress() diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go index f58b17879..3b024d9f9 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -2,7 +2,6 @@ package cli import ( "encoding/hex" - "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -31,7 +30,6 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) from, err := cliCtx.GetFromAddress() diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index ad3646ea1..a3508f880 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -1,8 +1,6 @@ package cli import ( - "os" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" @@ -24,7 +22,6 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) valAddr, err := cliCtx.GetFromAddress() diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index c68c12a5d..41c8dd312 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,7 +2,6 @@ package cli import ( "fmt" - "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -26,7 +25,6 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) amounstStr := viper.GetString(FlagAmount) @@ -116,7 +114,6 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) valAddr, err := cliCtx.GetFromAddress() @@ -169,7 +166,6 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) amount, err := sdk.ParseCoin(viper.GetString(FlagAmount)) @@ -228,7 +224,6 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) var err error @@ -284,7 +279,6 @@ func GetCmdCompleteRedelegate(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) delAddr, err := cliCtx.GetFromAddress() @@ -342,7 +336,6 @@ func GetCmdBeginUnbonding(storeName string, cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) delAddr, err := cliCtx.GetFromAddress() @@ -391,7 +384,6 @@ func GetCmdCompleteUnbonding(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithLogger(os.Stdout). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) delAddr, err := cliCtx.GetFromAddress() From a3b31c7a6b0c0a85ea8ea7612e087d2105e85a0d Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Tue, 2 Oct 2018 13:58:58 +0200 Subject: [PATCH 02/15] Merge PR #2427: Fix typo val faq --- docs/validators/validator-faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/validators/validator-faq.md b/docs/validators/validator-faq.md index 14644daf2..454bb21a1 100644 --- a/docs/validators/validator-faq.md +++ b/docs/validators/validator-faq.md @@ -216,12 +216,12 @@ We have to solve this simple equation to find the reward R for each validator: * For the proposer validator: * The pool obtains `R + R * 5%`: 105 Atoms * Commission: `105 * 80% * 1%` = 0.84 Atoms - * Validator's reward: `100 * 20% + Commission` = 21.84 Atoms + * Validator's reward: `105 * 20% + Commission` = 21.84 Atoms * Delegators' rewards: `105 * 80% - Commission` = 83.16 Atoms (each delegator will be able to claim its portion of these rewards in proportion to their stake) * For each non-proposer validator: * The pool obtains R: 100 Atoms * Commission: `100 * 80% * 1%` = 0.8 Atoms - * Validator's reward: `105 * 20% + Commission` = 20.8 Atoms + * Validator's reward: `100 * 20% + Commission` = 20.8 Atoms * Delegators' rewards: `100 * 80% - Commission` = 79.2 Atoms (each delegator will be able to claim its portion of these rewards in proportion to their stake) ### What are the slashing conditions? From 9410b6287c8890a9ce21b011d72347c9a669b70c Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 2 Oct 2018 05:01:18 -0700 Subject: [PATCH 03/15] Merge PR #2425: simulation: Make bank testing auth configurable --- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 2 +- x/bank/simulation/msgs.go | 124 ++++++++++++++++++++++------------ x/bank/simulation/sim_test.go | 5 +- 4 files changed, 85 insertions(+), 47 deletions(-) diff --git a/PENDING.md b/PENDING.md index 7458a110a..f36d4fd3e 100644 --- a/PENDING.md +++ b/PENDING.md @@ -138,6 +138,7 @@ IMPROVEMENTS * [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282 * [simulation] Add a concept of weighting the operations \#2303 * [simulation] Logs get written to file if large, and also get printed on panics \#2285 + * [simulation] Bank simulations now makes testing auth configurable \#2425 * [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable * [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149) * [store] [\#2017](https://github.com/cosmos/cosmos-sdk/issues/2017) Refactor diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 6097b5a08..db08d872f 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -90,7 +90,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ - {100, banksim.SimulateSingleInputMsgSend(app.accountMapper)}, + {100, banksim.SingleInputSendMsg(app.accountMapper, app.bankKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)}, {100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)}, {100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)}, diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index 0b8ec3026..0a253235e 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -15,47 +15,36 @@ import ( "github.com/tendermint/tendermint/crypto" ) -// SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both +// SingleInputSendTx tests and runs a single msg send w/ auth, with one input and one output, where both // accounts already exist. -func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation { +func SingleInputSendTx(mapper auth.AccountMapper) simulation.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromAcc := simulation.RandomAcc(r, accs) - toAcc := simulation.RandomAcc(r, accs) - // Disallow sending money to yourself - for { - if !fromAcc.PubKey.Equals(toAcc.PubKey) { - break - } - toAcc = simulation.RandomAcc(r, accs) + fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper) + if abort { + return action, nil, nil } - toAddr := toAcc.Address - initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins() - - if len(initFromCoins) == 0 { - return "skipping, no coins at all", nil, nil + err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, nil) + if err != nil { + return "", nil, err } + event("bank/sendAndVerifyTxSend/ok") - denomIndex := r.Intn(len(initFromCoins)) - amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount) - if goErr != nil { - return "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, nil, nil + return action, nil, nil + } +} + +// SingleInputSendMsg tests and runs a single msg send, with one input and one output, where both +// accounts already exist. +func SingleInputSendMsg(mapper auth.AccountMapper, bk bank.Keeper) simulation.Operation { + handler := bank.NewHandler(bk) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper) + if abort { + return action, nil, nil } - - action = fmt.Sprintf("%s is sending %s %s to %s", - fromAcc.Address.String(), - amt.String(), - initFromCoins[denomIndex].Denom, - toAddr.String(), - ) - - coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} - var msg = bank.MsgSend{ - Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, - Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, - } - goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}) - if goErr != nil { - return "", nil, goErr + err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler) + if err != nil { + return "", nil, err } event("bank/sendAndVerifyMsgSend/ok") @@ -63,8 +52,47 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation } } +func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper auth.AccountMapper) (fromAcc simulation.Account, action string, msg bank.MsgSend, abort bool) { + fromAcc = simulation.RandomAcc(r, accs) + toAcc := simulation.RandomAcc(r, accs) + // Disallow sending money to yourself + for { + if !fromAcc.PubKey.Equals(toAcc.PubKey) { + break + } + toAcc = simulation.RandomAcc(r, accs) + } + toAddr := toAcc.Address + initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins() + + if len(initFromCoins) == 0 { + return fromAcc, "skipping, no coins at all", msg, true + } + + denomIndex := r.Intn(len(initFromCoins)) + amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount) + if goErr != nil { + return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, true + } + + action = fmt.Sprintf("%s is sending %s %s to %s", + fromAcc.Address.String(), + amt.String(), + initFromCoins[denomIndex].Denom, + toAddr.String(), + ) + + coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} + msg = bank.MsgSend{ + Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, + Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, + } + return +} + // Sends and verifies the transition of a msg send. This fails if there are repeated inputs or outputs -func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey) error { +// pass in handler as nil to handle txs, otherwise handle msgs +func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error { initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs)) initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs)) AccountNumbers := make([]int64, len(msg.Inputs)) @@ -80,14 +108,22 @@ func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg b acc := mapper.GetAccount(ctx, msg.Outputs[i].Address) initialOutputAddrCoins[i] = acc.GetCoins() } - tx := mock.GenTx([]sdk.Msg{msg}, - AccountNumbers, - SequenceNumbers, - privkeys...) - res := app.Deliver(tx) - if !res.IsOK() { - // TODO: Do this in a more 'canonical' way - return fmt.Errorf("Deliver failed %v", res) + if handler != nil { + res := handler(ctx, msg) + if !res.IsOK() { + // TODO: Do this in a more 'canonical' way + return fmt.Errorf("handling msg failed %v", res) + } + } else { + tx := mock.GenTx([]sdk.Msg{msg}, + AccountNumbers, + SequenceNumbers, + privkeys...) + res := app.Deliver(tx) + if !res.IsOK() { + // TODO: Do this in a more 'canonical' way + return fmt.Errorf("Deliver failed %v", res) + } } for i := 0; i < len(msg.Inputs); i++ { diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index 819289845..ce3877a62 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -32,14 +32,15 @@ func TestBankWithRandomMessages(t *testing.T) { simulation.Simulate( t, mapp.BaseApp, appStateFn, []simulation.WeightedOperation{ - {1, SimulateSingleInputMsgSend(mapper)}, + {1, SingleInputSendTx(mapper)}, + {1, SingleInputSendMsg(mapper, bankKeeper)}, }, []simulation.RandSetup{}, []simulation.Invariant{ NonnegativeBalanceInvariant(mapper), TotalCoinsInvariant(mapper, func() sdk.Coins { return mapp.TotalCoinsSupply }), }, - 30, 30, + 30, 60, false, ) } From 6cbac799125414d225530241cc8ee44be36a76f7 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 2 Oct 2018 05:02:22 -0700 Subject: [PATCH 04/15] Merge PR #2419: Add quick start section to the docs readme --- docs/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/README.md b/docs/README.md index 48b8c69d5..2edcebe8d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,6 +8,12 @@ The first blockchain in the Cosmos Network is the Cosmos Hub, whose native token Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary. +## Quick Start + +- [Getting started with the SDK](./sdk/core/intro.html) +- [SDK Examples](../examples) +- [Join the testnet](./getting-started/full-node.html#run-a-full-node) + ## Edit the Documentation See [this file](./DOCS_README.md) for details of the build process and From 482a22da900f485340c229adf5853c95b6a39093 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 3 Oct 2018 17:27:59 +0200 Subject: [PATCH 05/15] Merge PR #2428: Update priorities file --- docs/PRIORITIES.md | 61 ++++++++++++---------------------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/docs/PRIORITIES.md b/docs/PRIORITIES.md index d5e425776..20eb20e1a 100644 --- a/docs/PRIORITIES.md +++ b/docs/PRIORITIES.md @@ -1,75 +1,44 @@ -# High priority - -## Fees - -- Collection - - Simple flat fee set in-config by validators & full nodes - ref [#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) - - @sunnya97 working on implementation - - _*BLOCKER:*_ Blocked on [tendermint/tendermint#2275](https://github.com/tendermint/tendermint/issues/2275) @ValarDragon -- Distribution - - "Piggy bank" fee distribution - ref [#1944](https://github.com/cosmos/cosmos-sdk/pull/1944) (spec) - - @rigelrozanski working on implementation -- EST TIMELINE: - - Work on fees should be completed in the `v0.25.0` release +# Post-0.25/GoS Pre-Release ## Staking/Slashing/Stability -- Unbonding state for validators (WIP) [#2163](https://github.com/cosmos/cosmos-sdk/pull/2163) @rigelrozanski - - Needs :eyes: from @chris - - Should be in `v0.25.0` release -- Slashing period PR - ref [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122) - - Needs :eyes: from @cwgoes and @jaekwon - - Should be in `v0.25.0` release - Other slashing issues blocking for launch - [#1256](https://github.com/cosmos/cosmos-sdk/issues/1256) -- Update staking/slashing for NextValSet change - - @cwgoes to start next - Miscellaneous minor staking issues - [List here](https://github.com/cosmos/cosmos-sdk/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Astaking+label%3Aprelaunch) - Need to figure out scope of work here to estimate time - @rigelrozanski to start next - -## Vesting - -- Single `VestingAccount` allowing delegation/voting but no withdrawals -- Ref [#1875](https://github.com/cosmos/cosmos-sdk/pull/1875) (spec) -- @AdityaSripal working on this. - - Should be in `v0.25.0` release +- Consider "tombstone" / "prison" - double-sign and you can never validate again - https://github.com/cosmos/cosmos-sdk/issues/2363 ## Multisig -- Already implemented on TM side, need simple CLI interface -- @alessio working on the SDK side of things here -- Need to schedule some time with @alessio, @ebuchman and @ValarDragon this week to finalize feature set/implementation plan +- Need to test changes in https://github.com/cosmos/cosmos-sdk/pull/2165 +- Spam prevention - https://github.com/cosmos/cosmos-sdk/issues/2019 ## ABCI Changes -- Need to update for new ABCI changes - error string, tags are list of lists, proposer in header (Tendermint 0.24?) -- @cwgoes has done some work here. Should be on `develop` in tendermint w/in next week. -- Include in tendermint `v0.24.0` release? +- Need to update for new ABCI changes when/if they land - error string, tags are list of lists +- Need to verify correct proposer reward semantics +- CheckEvidence/DeliverEvidence, CheckTx/DeliverTx ordering semantics ## Gas -- Simple transaction benchmarking work by @jlandrews to inform additional work here -- Integrate @alessio's simulation work into CLI and LCD -- Sanity Checks +- Charge for transaction size +- Decide what "one gas" corresponds to (standard hardware benchmarks?) +- More benchmarking +- Consider charging based on maximum depth of IAVL tree iteration +- Test out gas estimation in CLI and LCD and ensure the UX works ## LCD -- Bianje working on implementation ([#2147](https://github.com/cosmos/cosmos-sdk/pull/2147)) - - ICS 0,ICS 1, ICS 20 and ICS 21 implemented in this PR :point_up: - - @fedekunze, @jackzampolin and @alexanderbez to review +- Bianje working on implementation of ICS standards - Additional PR incoming for ICS 22 and ICS 23 -- Include [#382](https://github.com/cosmos/cosmos-sdk/issues/382) +- Decide what ought to be ICS-standardized and what ought not to # Lower priority ## Governance v2 -- Simple software upgrade proposals - - Implementation described in [#1079](https://github.com/cosmos/cosmos-sdk/issues/1079) - - Agree upon a block height to switch to new version -- Another Governance proposal from @jaekwon [#2116](https://github.com/cosmos/cosmos-sdk/pull/2116) -- Circuit breaker +- Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926 - Parameter change proposals (roughly the same implementation as circuit breaker) ## Documentation From 17983460b8f217b95220e8d261bda266ca7471dc Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 3 Oct 2018 17:48:23 +0200 Subject: [PATCH 06/15] Merge PR #2219: Update to Tendermint 0.24 (except NextValSet offsets) --- Gopkg.lock | 72 +++++++------ Gopkg.toml | 4 +- Makefile | 2 +- PENDING.md | 4 + baseapp/baseapp.go | 12 +-- client/config.go | 14 +-- client/context/context.go | 32 ++++-- client/context/query.go | 18 ++-- client/lcd/certificates.go | 2 +- client/lcd/test_helpers.go | 9 +- client/rpc/block.go | 2 +- client/rpc/status.go | 2 +- client/rpc/validators.go | 6 +- client/tx/query.go | 4 +- cmd/gaia/app/app_test.go | 2 +- cmd/gaia/app/benchmarks/txsize_test.go | 2 +- cmd/gaia/cli_test/cli_test.go | 2 +- cmd/gaia/cmd/gaiacli/main.go | 6 +- crypto/encode_test.go | 1 + crypto/keys/keybase.go | 2 +- crypto/keys/keybase_test.go | 4 +- examples/basecoin/app/app_test.go | 4 +- examples/democoin/app/app_test.go | 2 +- server/start.go | 7 ++ store/cachekvstore_test.go | 5 +- store/firstlast.go | 6 +- store/iavlstore.go | 4 +- store/iavlstore_test.go | 8 +- tools/gometalinter.json | 5 +- types/address_test.go | 2 +- types/coin_test.go | 4 +- types/context.go | 12 +-- types/context_test.go | 8 +- types/errors.go | 15 +-- types/stake.go | 2 - types/utils.go | 2 +- x/mock/simulation/random_simulate_blocks.go | 37 ++++--- x/mock/simulation/types.go | 2 +- x/slashing/keeper.go | 2 +- x/slashing/tick.go | 5 +- x/slashing/tick_test.go | 6 +- x/stake/genesis.go | 6 +- x/stake/genesis_test.go | 8 +- x/stake/handler.go | 4 +- x/stake/keeper/validator.go | 22 ++-- x/stake/keeper/validator_test.go | 114 ++++++++++---------- x/stake/types/validator.go | 25 +++-- x/stake/types/validator_test.go | 8 +- 48 files changed, 286 insertions(+), 241 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 00eafa4ad..5160d752e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,11 +34,11 @@ [[projects]] branch = "master" - digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" + digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "d81d8877b8f327112e94e814937143a71d1692a7" + revision = "2a560b2036bee5e3679ec2133eb6520b2f195213" [[projects]] digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" @@ -95,12 +95,12 @@ version = "v0.3.0" [[projects]] - digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406" + digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" name = "github.com/go-stack/stack" packages = ["."] pruneopts = "UT" - revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" - version = "v1.7.0" + revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" + version = "v1.8.0" [[projects]] digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" @@ -164,13 +164,13 @@ version = "v1.2.0" [[projects]] - branch = "master" - digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240" + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", + "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -179,7 +179,8 @@ "json/token", ] pruneopts = "UT" - revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" + revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" + version = "v1.0.0" [[projects]] digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" @@ -214,12 +215,12 @@ version = "v1.8.0" [[projects]] - digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb" + digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5" name = "github.com/mattn/go-isatty" packages = ["."] pruneopts = "UT" - revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" - version = "v0.0.3" + revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c" + version = "v0.0.4" [[projects]] digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" @@ -301,7 +302,7 @@ [[projects]] branch = "master" - digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" + digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef" name = "github.com/prometheus/procfs" packages = [ ".", @@ -310,7 +311,7 @@ "xfs", ] pruneopts = "UT" - revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92" + revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2" [[projects]] digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" @@ -320,15 +321,15 @@ revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" + digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" name = "github.com/spf13/afero" packages = [ ".", "mem", ] pruneopts = "UT" - revision = "787d034dfe70e44075ccc060d346146ef53270ad" - version = "v1.1.1" + revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" + version = "v1.1.2" [[projects]] digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" @@ -347,12 +348,12 @@ version = "v0.0.1" [[projects]] - branch = "master" - digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a" + digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" name = "github.com/spf13/jwalterweatherman" packages = ["."] pruneopts = "UT" - revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" + revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" + version = "v1.0.0" [[projects]] digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" @@ -402,6 +403,13 @@ pruneopts = "UT" revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" +[[projects]] + digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f" + name = "github.com/tendermint/btcd" + packages = ["btcec"] + pruneopts = "UT" + revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" + [[projects]] branch = "master" digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" @@ -415,12 +423,12 @@ revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" [[projects]] - digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee" + digest = "1:2c971a45c89ca2ccc735af50919cdee05fbdc54d4bf50625073693300e31ead8" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "UT" - revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c" - version = "v0.12.0-rc0" + revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" + version = "v0.12.0" [[projects]] digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430" @@ -431,7 +439,7 @@ version = "v0.11.0" [[projects]] - digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6" + digest = "1:f4fcc1a4dbe079b200556ca26c1ff1dacf23712125b9c265d8f02c0dbc318f39" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -449,6 +457,8 @@ "crypto/ed25519", "crypto/encoding/amino", "crypto/merkle", + "crypto/multisig", + "crypto/multisig/bitarray", "crypto/secp256k1", "crypto/tmhash", "crypto/xsalsa20symmetric", @@ -468,7 +478,6 @@ "lite", "lite/client", "lite/errors", - "lite/files", "lite/proxy", "mempool", "node", @@ -491,11 +500,12 @@ "state/txindex/kv", "state/txindex/null", "types", + "types/time", "version", ] pruneopts = "UT" - revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e" - version = "v0.23.1-rc0" + revision = "d419fffe18531317c28c29a292ad7d253f6cafdf" + version = "v0.24.0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" @@ -526,7 +536,7 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" + revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" [[projects]] digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" @@ -546,14 +556,14 @@ [[projects]] branch = "master" - digest = "1:ead82e3e398388679f3ad77633a087ac31a47a6be59ae20841e1d1b3a3fbbd22" + digest = "1:68023dc297a659d5eb2dafd62eda811456b338c5b3ec3c27da79e8a47d3f456a" name = "golang.org/x/sys" packages = [ "cpu", "unix", ] pruneopts = "UT" - revision = "4ea2f632f6e912459fe60b26b1749377f0d889d5" + revision = "2f1df4e56cdeb503a08d8577e6f1a7eb12efab82" [[projects]] digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" @@ -580,11 +590,11 @@ [[projects]] branch = "master" - digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c" + digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "UT" - revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" + revision = "0e822944c569bf5c9afd034adaa56208bd2906ac" [[projects]] digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" diff --git a/Gopkg.toml b/Gopkg.toml index 3080e3cd3..ca9cac0d8 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -49,7 +49,7 @@ [[override]] name = "github.com/tendermint/go-amino" - version = "=v0.12.0-rc0" + version = "=v0.12.0" [[override]] name = "github.com/tendermint/iavl" @@ -57,7 +57,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "=v0.23.1-rc0" + version = "=0.24.0" [[constraint]] name = "github.com/bartekn/go-bip39" diff --git a/Makefile b/Makefile index 49f15b375..3e0420072 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,7 @@ test_cover: test_lint: gometalinter.v2 --config=tools/gometalinter.json ./... - !(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") + !(gometalinter.v2 --exclude /usr/lib/go/src/ --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s dep status >> /dev/null !(grep -n branch Gopkg.toml) diff --git a/PENDING.md b/PENDING.md index f36d4fd3e..05c677265 100644 --- a/PENDING.md +++ b/PENDING.md @@ -42,6 +42,10 @@ BREAKING CHANGES * [simulation] \#2162 Added back correct supply invariants * SDK + * [core] \#2219 Update to Tendermint 0.24.0 + * Validator set updates delayed by one block + * BFT timestamp that can safely be used by applications + * Fixed maximum block size enforcement * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal * [types] [\#1901](https://github.com/cosmos/cosmos-sdk/issues/1901) Validator interface's GetOwner() renamed to GetOperator() * [x/slashing] [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122) - Implement slashing period diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cbf81d87d..34c27c1c6 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -64,9 +64,9 @@ type BaseApp struct { // checkState is set on initialization and reset on Commit. // deliverState is set in InitChain and BeginBlock and cleared on Commit. // See methods setCheckState and setDeliverState. - checkState *state // for CheckTx - deliverState *state // for DeliverTx - signedValidators []abci.SigningValidator // absent validators from begin block + checkState *state // for CheckTx + deliverState *state // for DeliverTx + voteInfos []abci.VoteInfo // absent validators from begin block // minimum fees for spam prevention minimumFees sdk.Coins @@ -435,7 +435,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // set the signed validators for addition to context in deliverTx // TODO: communicate this result to the address to pubkey map in slashing - app.signedValidators = req.LastCommitInfo.GetValidators() + app.voteInfos = req.LastCommitInfo.GetVotes() return } @@ -509,12 +509,12 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { } // retrieve the context for the ante handler and store the tx bytes; store -// the signing validators if the tx runs within the deliverTx() state. +// the vote infos if the tx runs within the deliverTx() state. func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { // Get the context ctx = getState(app, mode).ctx.WithTxBytes(txBytes) if mode == runTxModeDeliver { - ctx = ctx.WithSigningValidators(app.signedValidators) + ctx = ctx.WithVoteInfos(app.voteInfos) } return } diff --git a/client/config.go b/client/config.go index fcb252375..a1d38a016 100644 --- a/client/config.go +++ b/client/config.go @@ -1,15 +1,15 @@ package client import ( - "github.com/spf13/cobra" - "github.com/mitchellh/go-homedir" "bufio" - "path" - "os" - "io/ioutil" - "github.com/pelletier/go-toml" "fmt" "github.com/cosmos/cosmos-sdk/types" + "github.com/mitchellh/go-homedir" + "github.com/pelletier/go-toml" + "github.com/spf13/cobra" + "io/ioutil" + "os" + "path" ) type cliConfig struct { @@ -34,7 +34,7 @@ func ConfigCmd() *cobra.Command { return cfg } -func runConfigCmd(cmd *cobra.Command, args [] string) error { +func runConfigCmd(cmd *cobra.Command, args []string) error { home, err := homedir.Dir() if err != nil { return err diff --git a/client/context/context.go b/client/context/context.go index f93240d95..4f106d857 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "io" + "os" + "path/filepath" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -15,14 +17,18 @@ import ( cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" tmlite "github.com/tendermint/tendermint/lite" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" - "os" ) const ctxAccStoreName = "acc" +var ( + verifier tmlite.Verifier +) + // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. type CLIContext struct { @@ -39,7 +45,7 @@ type CLIContext struct { Async bool JSON bool PrintResponse bool - Certifier tmlite.Certifier + Verifier tmlite.Verifier DryRun bool GenerateOnly bool fromAddress types.AccAddress @@ -59,6 +65,11 @@ func NewCLIContext() CLIContext { from := viper.GetString(client.FlagFrom) fromAddress, fromName := fromFields(from) + // We need to use a single verifier for all contexts + if verifier == nil { + verifier = createVerifier() + } + return CLIContext{ Client: rpc, Output: os.Stdout, @@ -71,7 +82,7 @@ func NewCLIContext() CLIContext { Async: viper.GetBool(client.FlagAsync), JSON: viper.GetBool(client.FlagJson), PrintResponse: viper.GetBool(client.FlagPrintResponse), - Certifier: createCertifier(), + Verifier: verifier, DryRun: viper.GetBool(client.FlagDryRun), GenerateOnly: viper.GetBool(client.FlagGenerateOnly), fromAddress: fromAddress, @@ -79,7 +90,7 @@ func NewCLIContext() CLIContext { } } -func createCertifier() tmlite.Certifier { +func createVerifier() tmlite.Verifier { trustNodeDefined := viper.IsSet(client.FlagTrustNode) if !trustNodeDefined { return nil @@ -108,15 +119,16 @@ func createCertifier() tmlite.Certifier { fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String()) os.Exit(1) } + node := rpcclient.NewHTTP(nodeURI, "/websocket") + verifier, err := tmliteProxy.NewVerifier(chainID, filepath.Join(home, ".gaialite"), node, log.NewNopLogger()) - certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI) if err != nil { - fmt.Printf("Create certifier failed: %s\n", err.Error()) + fmt.Printf("Create verifier failed: %s\n", err.Error()) fmt.Printf("Please check network connection and verify the address of the node to connect to\n") os.Exit(1) } - return certifier + return verifier } func fromFields(from string) (fromAddr types.AccAddress, fromName string) { @@ -207,8 +219,8 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext { return ctx } -// WithCertifier - return a copy of the context with an updated Certifier -func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext { - ctx.Certifier = certifier +// WithVerifier - return a copy of the context with an updated Verifier +func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext { + ctx.Verifier = verifier return ctx } diff --git a/client/context/query.go b/client/context/query.go index d9bef3f5d..e4e488190 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -14,10 +14,10 @@ import ( "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/lite" tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" + tmtypes "github.com/tendermint/tendermint/types" ) // GetNode returns an RPC client. If the context's client is not defined, an @@ -184,14 +184,14 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro return resp.Value, nil } -// Certify verifies the consensus proof at given height. -func (ctx CLIContext) Certify(height int64) (lite.Commit, error) { - check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Certifier) +// Verify verifies the consensus proof at given height. +func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) { + check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier) switch { - case tmliteErr.IsCommitNotFoundErr(err): - return lite.Commit{}, ErrVerifyCommit(height) + case tmliteErr.IsErrCommitNotFound(err): + return tmtypes.SignedHeader{}, ErrVerifyCommit(height) case err != nil: - return lite.Commit{}, err + return tmtypes.SignedHeader{}, err } return check, nil @@ -199,12 +199,12 @@ func (ctx CLIContext) Certify(height int64) (lite.Commit, error) { // verifyProof perform response proof verification. func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { - if ctx.Certifier == nil { + if ctx.Verifier == nil { return fmt.Errorf("missing valid certifier to verify data from distrusted node") } // the AppHash for height H is in header H+1 - commit, err := ctx.Certify(resp.Height + 1) + commit, err := ctx.Verify(resp.Height + 1) if err != nil { return err } diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go index f47f2397c..1516ed35a 100644 --- a/client/lcd/certificates.go +++ b/client/lcd/certificates.go @@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, - IsCA: true, + IsCA: true, } hosts := strings.Split(host, ",") for _, h := range hosts { diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 3f6013822..478620d33 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -33,6 +33,7 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" nm "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" @@ -186,7 +187,8 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress // XXX: Need to set this so LCD knows the tendermint node address! viper.Set(client.FlagNode, config.RPC.ListenAddress) viper.Set(client.FlagChainID, genDoc.ChainID) - viper.Set(client.FlagTrustNode, false) + // TODO Set to false once the upstream Tendermint proof verification issue is fixed. + viper.Set(client.FlagTrustNode, true) dir, err := ioutil.TempDir("", "lcd_test") require.NoError(t, err) viper.Set(cli.HomeFlag, dir) @@ -222,9 +224,14 @@ func startTM( ) (*nm.Node, error) { genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil } dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil } + nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile()) + if err != nil { + return nil, err + } node, err := nm.NewNode( tmcfg, privVal, + nodeKey, proxy.NewLocalClientCreator(app), genDocProvider, dbProvider, diff --git a/client/rpc/block.go b/client/rpc/block.go index 44def2d32..d734dc989 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -46,7 +46,7 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { } if !cliCtx.TrustNode { - check, err := cliCtx.Certify(res.Block.Height) + check, err := cliCtx.Verify(res.Block.Height) if err != nil { return nil, err } diff --git a/client/rpc/status.go b/client/rpc/status.go index 6c4270175..049910ed1 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/spf13/viper" + ctypes "github.com/tendermint/tendermint/rpc/core/types" ) func statusCommand() *cobra.Command { diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 4802e785b..e3fef4c6e 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/spf13/viper" + tmtypes "github.com/tendermint/tendermint/types" ) // TODO these next two functions feel kinda hacky based on their placement @@ -76,12 +76,12 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { } if !cliCtx.TrustNode { - check, err := cliCtx.Certify(validatorsRes.BlockHeight) + check, err := cliCtx.Verify(validatorsRes.BlockHeight) if err != nil { return nil, err } - if !bytes.Equal(check.ValidatorsHash(), tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) { + if !bytes.Equal(check.ValidatorsHash, tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) { return nil, fmt.Errorf("got invalid validatorset") } } diff --git a/client/tx/query.go b/client/tx/query.go index f1d13b608..a1b7e5552 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -3,8 +3,8 @@ package tx import ( "encoding/hex" "fmt" - "net/http" "github.com/tendermint/tendermint/libs/common" + "net/http" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -83,7 +83,7 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([] // ValidateTxResult performs transaction verification func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error { - check, err := cliCtx.Certify(res.Height) + check, err := cliCtx.Verify(res.Height) if err != nil { return err } diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 0fb45863c..bcd16a176 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -31,7 +31,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { } // Initialize the chain - vals := []abci.Validator{} + vals := []abci.ValidatorUpdate{} gapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes}) gapp.Commit() diff --git a/cmd/gaia/app/benchmarks/txsize_test.go b/cmd/gaia/app/benchmarks/txsize_test.go index 186ad87e5..24789b10e 100644 --- a/cmd/gaia/app/benchmarks/txsize_test.go +++ b/cmd/gaia/app/benchmarks/txsize_test.go @@ -31,5 +31,5 @@ func ExampleTxSendSize() { fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1}))) fmt.Println(len(cdc.MustMarshalBinaryBare(tx))) // output: 80 - // 173 + // 167 } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 5b204f4df..04bc4c84a 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -7,8 +7,8 @@ import ( "fmt" "io/ioutil" "os" - "testing" "path" + "testing" "github.com/stretchr/testify/require" diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 2f66caadd..5be9d76e8 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -18,9 +18,9 @@ import ( stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "path" - "os" "github.com/spf13/viper" + "os" + "path" ) const ( @@ -152,7 +152,7 @@ func initConfig(cmd *cobra.Command) error { } if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { - return err + return err } return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) } diff --git a/crypto/encode_test.go b/crypto/encode_test.go index 9e7097855..aef8d8e32 100644 --- a/crypto/encode_test.go +++ b/crypto/encode_test.go @@ -55,6 +55,7 @@ func ExamplePrintRegisteredTypes() { //| PrivKeyLedgerSecp256k1 | tendermint/PrivKeyLedgerSecp256k1 | 0x10CAB393 | variable | | //| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | //| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | + //| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | | //| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | //| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | } diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index c9bfd0812..99632e764 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -9,12 +9,12 @@ import ( "github.com/cosmos/cosmos-sdk/crypto" "github.com/cosmos/cosmos-sdk/crypto/keys/bip39" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" tmcrypto "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" - "github.com/cosmos/cosmos-sdk/types" ) var _ Keybase = dbKeybase{} diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 2a602461a..3273c229a 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -10,8 +10,8 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/cosmos/cosmos-sdk/types" + dbm "github.com/tendermint/tendermint/libs/db" ) func init() { @@ -403,4 +403,4 @@ func ExampleNew() { func accAddr(info Info) types.AccAddress { return (types.AccAddress)(info.GetPubKey().Address()) -} \ No newline at end of file +} diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 24237e602..da544f480 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -29,7 +29,7 @@ func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.Gene // initialize and commit the chain baseApp.InitChain(abci.RequestInitChain{ - Validators: []abci.Validator{}, AppStateBytes: stateBytes, + Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes, }) baseApp.Commit() @@ -72,7 +72,7 @@ func TestGenesis(t *testing.T) { // initialize the chain with the expected genesis state baseApp.InitChain(abci.RequestInitChain{ - Validators: []abci.Validator{}, AppStateBytes: stateBytes, + Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes, }) ctx = baseApp.BaseApp.NewContext(true, abci.Header{}) diff --git a/examples/democoin/app/app_test.go b/examples/democoin/app/app_test.go index eeea84bd4..fc00651b8 100644 --- a/examples/democoin/app/app_test.go +++ b/examples/democoin/app/app_test.go @@ -33,7 +33,7 @@ func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error } // Initialize the chain - vals := []abci.Validator{} + vals := []abci.ValidatorUpdate{} bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes}) bapp.Commit() diff --git a/server/start.go b/server/start.go index 5d5b1b3eb..170ff9dcb 100644 --- a/server/start.go +++ b/server/start.go @@ -10,6 +10,7 @@ import ( tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" ) @@ -97,10 +98,16 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } + nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) + if err != nil { + return nil, err + } + // create & start tendermint node tmNode, err := node.NewNode( cfg, pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()), + nodeKey, proxy.NewLocalClientCreator(app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, diff --git a/store/cachekvstore_test.go b/store/cachekvstore_test.go index 9d02d57d3..37e0364fb 100644 --- a/store/cachekvstore_test.go +++ b/store/cachekvstore_test.go @@ -1,6 +1,7 @@ package store import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -13,8 +14,8 @@ func newCacheKVStore() CacheKVStore { return NewCacheKVStore(mem) } -func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } -func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } +func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestCacheKVStore(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} diff --git a/store/firstlast.go b/store/firstlast.go index 70f6659a8..a47f1396d 100644 --- a/store/firstlast.go +++ b/store/firstlast.go @@ -14,7 +14,7 @@ func First(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) { } defer iter.Close() - return cmn.KVPair{iter.Key(), iter.Value()}, true + return cmn.KVPair{Key: iter.Key(), Value: iter.Value()}, true } // Gets the last item. `end` is exclusive. @@ -22,7 +22,7 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) { iter := st.ReverseIterator(end, start) if !iter.Valid() { if v := st.Get(start); v != nil { - return cmn.KVPair{cp(start), cp(v)}, true + return cmn.KVPair{Key: cp(start), Value: cp(v)}, true } return kv, false } @@ -36,5 +36,5 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) { } } - return cmn.KVPair{iter.Key(), iter.Value()}, true + return cmn.KVPair{Key: iter.Key(), Value: iter.Value()}, true } diff --git a/store/iavlstore.go b/store/iavlstore.go index d301da771..d535fd436 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -240,7 +240,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { var KVs []KVPair iterator := sdk.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { - KVs = append(KVs, KVPair{iterator.Key(), iterator.Value()}) + KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) } iterator.Close() res.Value = cdc.MustMarshalBinary(KVs) @@ -310,7 +310,7 @@ func (iter *iavlIterator) iterateRoutine() { select { case <-iter.quitCh: return true // done with iteration. - case iter.iterCh <- cmn.KVPair{key, value}: + case iter.iterCh <- cmn.KVPair{Key: key, Value: value}: return false // yay. } }, diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 49793d376..1e9263b7b 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -387,12 +387,12 @@ func TestIAVLStoreQuery(t *testing.T) { ksub := []byte("key") KVs0 := []KVPair{} KVs1 := []KVPair{ - {k1, v1}, - {k2, v2}, + {Key: k1, Value: v1}, + {Key: k2, Value: v2}, } KVs2 := []KVPair{ - {k1, v3}, - {k2, v2}, + {Key: k1, Value: v3}, + {Key: k2, Value: v2}, } valExpSubEmpty := cdc.MustMarshalBinary(KVs0) valExpSub1 := cdc.MustMarshalBinary(KVs1) diff --git a/tools/gometalinter.json b/tools/gometalinter.json index 42788714b..a7b9ab517 100644 --- a/tools/gometalinter.json +++ b/tools/gometalinter.json @@ -5,5 +5,6 @@ "Enable": ["golint", "vet", "ineffassign", "unparam", "unconvert", "misspell"], "Deadline": "500s", "Vendor": true, - "Cyclo": 11 -} \ No newline at end of file + "Cyclo": 11, + "Exclude": ["/usr/lib/go/src/"] +} diff --git a/types/address_test.go b/types/address_test.go index 6c6c78d6e..e2ec36876 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -10,7 +10,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/cosmos/cosmos-sdk/types" - ) +) var invalidStrs = []string{ "", diff --git a/types/coin_test.go b/types/coin_test.go index ba7038010..bc0441279 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -443,7 +443,7 @@ func BenchmarkCoinsAdditionIntersect(b *testing.B) { } } - benchmarkSizes := [][]int{[]int{1, 1}, []int{5, 5}, []int{5, 20}, []int{1, 1000}, []int{2, 1000}} + benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}} for i := 0; i < len(benchmarkSizes); i++ { sizeA := benchmarkSizes[i][0] sizeB := benchmarkSizes[i][1] @@ -469,7 +469,7 @@ func BenchmarkCoinsAdditionNoIntersect(b *testing.B) { } } - benchmarkSizes := [][]int{[]int{1, 1}, []int{5, 5}, []int{5, 20}, []int{1, 1000}, []int{2, 1000}, []int{1000, 2}} + benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}, {1000, 2}} for i := 0; i < len(benchmarkSizes); i++ { sizeA := benchmarkSizes[i][0] sizeB := benchmarkSizes[i][1] diff --git a/types/context.go b/types/context.go index 6a54f247d..fd2e07688 100644 --- a/types/context.go +++ b/types/context.go @@ -45,7 +45,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo c = c.WithIsCheckTx(isCheckTx) c = c.WithTxBytes(nil) c = c.WithLogger(logger) - c = c.WithSigningValidators(nil) + c = c.WithVoteInfos(nil) c = c.WithGasMeter(NewInfiniteGasMeter()) c = c.WithMinimumFees(Coins{}) return c @@ -138,7 +138,7 @@ const ( contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger - contextKeySigningValidators + contextKeyVoteInfos contextKeyGasMeter contextKeyMinimumFees ) @@ -164,8 +164,8 @@ func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) } func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) } -func (c Context) SigningValidators() []abci.SigningValidator { - return c.Value(contextKeySigningValidators).([]abci.SigningValidator) +func (c Context) VoteInfos() []abci.VoteInfo { + return c.Value(contextKeyVoteInfos).([]abci.VoteInfo) } func (c Context) GasMeter() GasMeter { return c.Value(contextKeyGasMeter).(GasMeter) } @@ -199,8 +199,8 @@ func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contex func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) } -func (c Context) WithSigningValidators(SigningValidators []abci.SigningValidator) Context { - return c.withValue(contextKeySigningValidators, SigningValidators) +func (c Context) WithVoteInfos(VoteInfos []abci.VoteInfo) Context { + return c.withValue(contextKeyVoteInfos, VoteInfos) } func (c Context) WithGasMeter(meter GasMeter) Context { return c.withValue(contextKeyGasMeter, meter) } diff --git a/types/context_test.go b/types/context_test.go index e08aca01f..002691229 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -151,7 +151,7 @@ func TestContextWithCustom(t *testing.T) { require.Panics(t, func() { ctx.ChainID() }) require.Panics(t, func() { ctx.TxBytes() }) require.Panics(t, func() { ctx.Logger() }) - require.Panics(t, func() { ctx.SigningValidators() }) + require.Panics(t, func() { ctx.VoteInfos() }) require.Panics(t, func() { ctx.GasMeter() }) header := abci.Header{} @@ -160,7 +160,7 @@ func TestContextWithCustom(t *testing.T) { ischeck := true txbytes := []byte("txbytes") logger := NewMockLogger() - signvals := []abci.SigningValidator{{}} + voteinfos := []abci.VoteInfo{{}} meter := types.NewGasMeter(10000) minFees := types.Coins{types.NewInt64Coin("feeCoin", 1)} @@ -168,7 +168,7 @@ func TestContextWithCustom(t *testing.T) { WithBlockHeight(height). WithChainID(chainid). WithTxBytes(txbytes). - WithSigningValidators(signvals). + WithVoteInfos(voteinfos). WithGasMeter(meter). WithMinimumFees(minFees) @@ -178,7 +178,7 @@ func TestContextWithCustom(t *testing.T) { require.Equal(t, ischeck, ctx.IsCheckTx()) require.Equal(t, txbytes, ctx.TxBytes()) require.Equal(t, logger, ctx.Logger()) - require.Equal(t, signvals, ctx.SigningValidators()) + require.Equal(t, voteinfos, ctx.VoteInfos()) require.Equal(t, meter, ctx.GasMeter()) require.Equal(t, minFees, types.Coins{types.NewInt64Coin("feeCoin", 1)}) } diff --git a/types/errors.go b/types/errors.go index 46bf748f4..266e6d14c 100644 --- a/types/errors.go +++ b/types/errors.go @@ -2,7 +2,6 @@ package types import ( "fmt" - "strings" "github.com/cosmos/cosmos-sdk/codec" cmn "github.com/tendermint/tendermint/libs/common" @@ -231,13 +230,12 @@ func (err *sdkError) TraceSDK(format string, args ...interface{}) Error { } // Implements ABCIError. -// Overrides err.Error.Error(). func (err *sdkError) Error() string { return fmt.Sprintf(`ERROR: Codespace: %d Code: %d Message: %#v -`, err.codespace, err.code, parseCmnError(err.cmnError.Error())) +`, err.codespace, err.code, err.cmnError.Error()) } // Implements ABCIError. @@ -258,12 +256,12 @@ func (err *sdkError) Code() CodeType { // Implements ABCIError. func (err *sdkError) ABCILog() string { cdc := codec.New() - parsedErrMsg := parseCmnError(err.cmnError.Error()) + errMsg := err.cmnError.Error() jsonErr := humanReadableError{ Codespace: err.codespace, Code: err.code, ABCICode: err.ABCICode(), - Message: parsedErrMsg, + Message: errMsg, } bz, er := cdc.MarshalJSON(jsonErr) if er != nil { @@ -288,13 +286,6 @@ func (err *sdkError) QueryResult() abci.ResponseQuery { } } -func parseCmnError(err string) string { - if idx := strings.Index(err, "{"); idx != -1 { - err = err[idx+1 : len(err)-1] - } - return err -} - // nolint type humanReadableError struct { Codespace CodespaceType `json:"codespace"` diff --git a/types/stake.go b/types/stake.go index 2f92639f3..c84ed8d05 100644 --- a/types/stake.go +++ b/types/stake.go @@ -3,7 +3,6 @@ package types import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" - tmtypes "github.com/tendermint/tendermint/types" ) // status of a validator @@ -52,7 +51,6 @@ type Validator interface { // validator which fulfills abci validator interface for use in Tendermint func ABCIValidator(v Validator) abci.Validator { return abci.Validator{ - PubKey: tmtypes.TM2PB.PubKey(v.GetConsPubKey()), Address: v.GetConsPubKey().Address(), Power: v.GetPower().RoundInt64(), } diff --git a/types/utils.go b/types/utils.go index 95fc779d7..b196acb23 100644 --- a/types/utils.go +++ b/types/utils.go @@ -51,4 +51,4 @@ func DefaultChainID() (string, error) { } return doc.ChainID, nil -} \ No newline at end of file +} diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 25748e15b..ee93cfea3 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -36,7 +36,12 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { - validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} + pubkey, err := tmtypes.PB2TM.PubKey(validator.PubKey) + if err != nil { + panic(err) + } + address := pubkey.Address() + validators[string(address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} } for i := 0; i < len(setups); i++ { @@ -91,9 +96,9 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, }() var pastTimes []time.Time - var pastSigningValidators [][]abci.SigningValidator + var pastVoteInfos [][]abci.VoteInfo - request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header) + request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) // These are operations which have been queued by previous operations operationQueue := make(map[int][]Operation) timeOperationQueue := []FutureOperation{} @@ -122,7 +127,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, 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) + pastVoteInfos = append(pastVoteInfos, request.LastCommitInfo.Votes) // Run the BeginBlock handler app.BeginBlock(request) @@ -157,7 +162,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } // Generate a random RequestBeginBlock with the current validator set for the next block - request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header) + request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) // Update the validator set validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event) @@ -316,11 +321,11 @@ func getKeys(validators map[string]mockValidator) []string { // RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction // nolint: unparam func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64, - pastTimes []time.Time, pastSigningValidators [][]abci.SigningValidator, event func(string), header abci.Header) abci.RequestBeginBlock { + pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock { if len(validators) == 0 { return abci.RequestBeginBlock{Header: header} } - signingValidators := make([]abci.SigningValidator, len(validators)) + voteInfos := make([]abci.VoteInfo, len(validators)) i := 0 for _, key := range getKeys(validators) { mVal := validators[key] @@ -341,8 +346,14 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, } else { event("beginblock/signing/missed") } - signingValidators[i] = abci.SigningValidator{ - Validator: mVal.val, + pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) + if err != nil { + panic(err) + } + voteInfos[i] = abci.VoteInfo{ + Validator: abci.Validator{ + Address: pubkey.Address(), + }, SignedLastBlock: signed, } i++ @@ -354,11 +365,11 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, for r.Float64() < evidenceFraction { height := header.Height time := header.Time - vals := signingValidators + vals := voteInfos if r.Float64() < pastEvidenceFraction { height = int64(r.Intn(int(header.Height))) time = pastTimes[height] - vals = pastSigningValidators[height] + vals = pastVoteInfos[height] } validator := vals[r.Intn(len(vals))].Validator var totalVotingPower int64 @@ -378,7 +389,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, return abci.RequestBeginBlock{ Header: header, LastCommitInfo: abci.LastCommitInfo{ - Validators: signingValidators, + Votes: voteInfos, }, ByzantineValidators: evidence, } @@ -386,7 +397,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, // updateValidators mimicks Tendermint's update logic // nolint: unparam -func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator { +func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { for _, update := range updates { switch { diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index 2e68a428e..302ebcbd7 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -45,7 +45,7 @@ type ( } mockValidator struct { - val abci.Validator + val abci.ValidatorUpdate livenessState int } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 6a5fa3014..0c1c70b36 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -138,7 +138,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p } // AddValidators adds the validators to the keepers validator addr to pubkey mapping. -func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.Validator) { +func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.ValidatorUpdate) { for i := 0; i < len(vals); i++ { val := vals[i] pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey) diff --git a/x/slashing/tick.go b/x/slashing/tick.go index 253daa412..007d93788 100644 --- a/x/slashing/tick.go +++ b/x/slashing/tick.go @@ -20,9 +20,8 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags // Iterate over all the validators which *should* have signed this block // Store whether or not they have actually signed it and slash/unbond any // which have missed too many blocks in a row (downtime slashing) - for _, signingValidator := range req.LastCommitInfo.GetValidators() { - present := signingValidator.SignedLastBlock - sk.handleValidatorSignature(ctx, signingValidator.Validator.Address, signingValidator.Validator.Power, present) + for _, voteInfo := range req.LastCommitInfo.GetVotes() { + sk.handleValidatorSignature(ctx, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock) } // Iterate through any newly discovered evidence of infraction diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 8225c9634..5f0ccf8ed 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -32,7 +32,7 @@ func TestBeginBlocker(t *testing.T) { // mark the validator as having signed req := abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ - Validators: []abci.SigningValidator{{ + Votes: []abci.VoteInfo{{ Validator: val, SignedLastBlock: true, }}, @@ -54,7 +54,7 @@ func TestBeginBlocker(t *testing.T) { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ - Validators: []abci.SigningValidator{{ + Votes: []abci.VoteInfo{{ Validator: val, SignedLastBlock: true, }}, @@ -68,7 +68,7 @@ func TestBeginBlocker(t *testing.T) { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ - Validators: []abci.SigningValidator{{ + Votes: []abci.VoteInfo{{ Validator: val, SignedLastBlock: false, }}, diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 58b7ed1b4..740eba415 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -15,7 +15,7 @@ import ( // addition, it also sets any delegations found in data. Finally, it updates // the bonded validators. // Returns final validator set after applying all declaration and delegations -func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.Validator, err error) { +func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) { keeper.SetPool(ctx, data.Pool) keeper.SetNewParams(ctx, data.Params) keeper.InitIntraTxCounter(ctx) @@ -47,9 +47,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ keeper.UpdateBondedValidatorsFull(ctx) vals := keeper.GetValidatorsBonded(ctx) - res = make([]abci.Validator, len(vals)) + res = make([]abci.ValidatorUpdate, len(vals)) for i, val := range vals { - res[i] = sdk.ABCIValidator(val) + res[i] = val.ABCIValidatorUpdate() } return } diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index 947bb2e18..0051ff252 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -59,9 +59,9 @@ func TestInitGenesis(t *testing.T) { require.Equal(t, sdk.Bonded, resVal.Status) require.Equal(t, int16(1), resVal.BondIntraTxCounter) - abcivals := make([]abci.Validator, len(vals)) + abcivals := make([]abci.ValidatorUpdate, len(vals)) for i, val := range validators { - abcivals[i] = sdk.ABCIValidator(val) + abcivals[i] = val.ABCIValidatorUpdate() } require.Equal(t, abcivals, vals) @@ -98,9 +98,9 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { vals, err := InitGenesis(ctx, keeper, genesisState) require.NoError(t, err) - abcivals := make([]abci.Validator, 100) + abcivals := make([]abci.ValidatorUpdate, 100) for i, val := range validators[:100] { - abcivals[i] = sdk.ABCIValidator(val) + abcivals[i] = val.ABCIValidatorUpdate() } require.Equal(t, abcivals, vals) diff --git a/x/stake/handler.go b/x/stake/handler.go index 8b53065cc..65915a65e 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -36,7 +36,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } // Called every block, process inflation, update validator set -func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) { +func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) { pool := k.GetPool(ctx) // Process provision inflation @@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid k.SetIntraTxCounter(ctx, 0) // calculate validator set changes - ValidatorUpdates = k.GetValidTendermintUpdates(ctx) + ValidatorUpdates = k.GetTendermintUpdates(ctx) return } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 84a82dba5..668a2af23 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -6,6 +6,7 @@ import ( "fmt" abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -194,19 +195,22 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { // CONTRACT: Only validators with non-zero power or zero-power that were bonded // at the previous block height or were removed from the validator set entirely // are returned to Tendermint. -func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) { +func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) { tstore := ctx.TransientStore(k.storeTKey) iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey) defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - var abciVal abci.Validator + var abciVal abci.ValidatorUpdate abciValBytes := iterator.Value() k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal) - val, found := k.GetValidator(ctx, abciVal.GetAddress()) + pub, err := tmtypes.PB2TM.PubKey(abciVal.GetPubKey()) + if err != nil { + panic(err) + } + val, found := k.GetValidator(ctx, sdk.ValAddress(pub.Address())) if found { // The validator is new or already exists in the store and must adhere to // Tendermint invariants. @@ -258,7 +262,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type case powerIncreasing && !validator.Jailed && (oldFound && oldValidator.Status == sdk.Bonded): - bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) + bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) if cliffValExists { @@ -293,7 +297,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()) + bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) } } @@ -610,7 +614,7 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat k.SetValidator(ctx, validator) // add to accumulated changes for tendermint - bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero()) + bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero()) tstore := ctx.TransientStore(k.storeTKey) tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) @@ -643,7 +647,7 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{}) // add to accumulated changes for tendermint - bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator()) + bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) tstore := ctx.TransientStore(k.storeTKey) tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) @@ -676,7 +680,7 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { } store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) - bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero()) + bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero()) tstore := ctx.TransientStore(k.storeTKey) tstore.Set(GetTendermintUpdatesTKey(address), bz) } diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 73a391acc..7533fed6e 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -71,9 +71,9 @@ func TestSetValidator(t *testing.T) { require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validator.ABCIValidator(), updates[0]) + require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) allVals := keeper.GetAllValidators(ctx) require.Equal(t, 1, len(allVals)) @@ -675,7 +675,7 @@ func TestFullValidatorSetPowerChange(t *testing.T) { assert.True(ValEq(t, validators[2], resValidators[1])) } -func TestGetValidTendermintUpdatesAllNone(t *testing.T) { +func TestGetTendermintUpdatesAllNone(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -693,17 +693,17 @@ func TestGetValidTendermintUpdatesAllNone(t *testing.T) { // test from nothing to something // tendermintUpdate set: {} -> {c1, c3} - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) assert.Equal(t, 2, len(updates)) - assert.Equal(t, validators[0].ABCIValidator(), updates[0]) - assert.Equal(t, validators[1].ABCIValidator(), updates[1]) + assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } -func TestGetValidTendermintUpdatesIdentical(t *testing.T) { +func TestGetTendermintUpdatesIdentical(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -717,16 +717,16 @@ func TestGetValidTendermintUpdatesIdentical(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(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.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) } -func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) { +func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -740,7 +740,7 @@ func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // test single value change // tendermintUpdate set: {} -> {c1'} @@ -748,13 +748,13 @@ func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) { validators[0].Tokens = sdk.NewDec(600) validators[0] = keeper.UpdateValidator(ctx, validators[0]) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validators[0].ABCIValidator(), updates[0]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) } -func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) { +func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -768,7 +768,7 @@ func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} @@ -779,13 +779,13 @@ func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 2, len(updates)) - require.Equal(t, validators[0].ABCIValidator(), updates[0]) - require.Equal(t, validators[1].ABCIValidator(), updates[1]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } -func TestGetValidTendermintUpdatesInserted(t *testing.T) { +func TestGetTendermintUpdatesInserted(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20, 5, 15, 25} @@ -799,33 +799,33 @@ func TestGetValidTendermintUpdatesInserted(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} validators[2] = keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validators[2].ABCIValidator(), updates[0]) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} clearTendermintUpdates(ctx, keeper) validators[3] = keeper.UpdateValidator(ctx, validators[3]) - updates = keeper.GetValidTendermintUpdates(ctx) + updates = keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validators[3].ABCIValidator(), updates[0]) + require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0]) // test validtor added at the end // tendermintUpdate set: {} -> {c0} clearTendermintUpdates(ctx, keeper) validators[4] = keeper.UpdateValidator(ctx, validators[4]) - updates = keeper.GetValidTendermintUpdates(ctx) + updates = keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validators[4].ABCIValidator(), updates[0]) + require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0]) } -func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) { +func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := types.DefaultParams() params.MaxValidators = 2 @@ -842,31 +842,31 @@ func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // test validator added at the end but not inserted in the valset // tendermintUpdate set: {} -> {} keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 0, len(updates)) // test validator change its power and become a gotValidator (pushing out an existing) // tendermintUpdate set: {} -> {c0, c4} clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(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.GetValidTendermintUpdates(ctx) + updates = keeper.GetTendermintUpdates(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]) + require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[0]) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[1]) } -func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) { +func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{100, 100} @@ -880,7 +880,7 @@ func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // check initial power require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64()) @@ -900,13 +900,13 @@ func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) { require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64()) // Tendermint updates should reflect power change - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 2, len(updates)) - require.Equal(t, validators[0].ABCIValidator(), updates[0]) - require.Equal(t, validators[1].ABCIValidator(), updates[1]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } -func TestGetValidTendermintUpdatesNewValidator(t *testing.T) { +func TestGetTendermintUpdatesNewValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := keeper.GetParams(ctx) params.MaxValidators = uint16(3) @@ -930,13 +930,13 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) { } // verify initial Tendermint updates are correct - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, len(validators), len(updates)) - require.Equal(t, validators[0].ABCIValidator(), updates[0]) - require.Equal(t, validators[1].ABCIValidator(), updates[1]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // update initial validator set for i, amt := range amts { @@ -974,14 +974,14 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) { validator = keeper.UpdateValidator(ctx, validator) // verify initial Tendermint updates are correct - updates = keeper.GetValidTendermintUpdates(ctx) + updates = keeper.GetTendermintUpdates(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]) + require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2]) } -func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { +func TestGetTendermintUpdatesBondTransition(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := keeper.GetParams(ctx) params.MaxValidators = uint16(2) @@ -1006,13 +1006,13 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { } // verify initial Tendermint updates are correct - updates := keeper.GetValidTendermintUpdates(ctx) + updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 2, len(updates)) - require.Equal(t, validators[2].ABCIValidator(), updates[0]) - require.Equal(t, validators[1].ABCIValidator(), updates[1]) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // delegate to validator with lowest power but not enough to bond ctx = ctx.WithBlockHeight(1) @@ -1027,7 +1027,7 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { validators[0] = keeper.UpdateValidator(ctx, validator) // verify initial Tendermint updates are correct - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // create a series of events that will bond and unbond the validator with // lowest power in a single block context (height) @@ -1048,12 +1048,12 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) { validators[1] = keeper.UpdateValidator(ctx, validator) // verify initial Tendermint updates are correct - updates = keeper.GetValidTendermintUpdates(ctx) + updates = keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) - require.Equal(t, validators[1].ABCIValidator(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) } func TestUpdateValidatorCommission(t *testing.T) { diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 051ffa9e5..57794691f 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -305,22 +305,21 @@ func (d Description) EnsureLength() (Description, sdk.Error) { return d, nil } -// ABCIValidator returns an abci.Validator from a staked validator type. -func (v Validator) ABCIValidator() abci.Validator { - return abci.Validator{ - PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), - Address: v.ConsPubKey.Address(), - Power: v.BondedTokens().RoundInt64(), +// ABCIValidatorUpdate returns an abci.ValidatorUpdate from a staked validator type +// with the full validator power +func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { + return abci.ValidatorUpdate{ + PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + Power: v.BondedTokens().RoundInt64(), } } -// ABCIValidatorZero returns an abci.Validator from a staked validator type -// with with zero power used for validator updates. -func (v Validator) ABCIValidatorZero() abci.Validator { - return abci.Validator{ - PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), - Address: v.ConsPubKey.Address(), - Power: 0, +// ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staked validator type +// with zero power used for validator updates. +func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { + return abci.ValidatorUpdate{ + PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + Power: 0, } } diff --git a/x/stake/types/validator_test.go b/x/stake/types/validator_test.go index de9e18480..36ac0da57 100644 --- a/x/stake/types/validator_test.go +++ b/x/stake/types/validator_test.go @@ -55,18 +55,18 @@ func TestUpdateDescription(t *testing.T) { require.Equal(t, d, d3) } -func TestABCIValidator(t *testing.T) { +func TestABCIValidatorUpdate(t *testing.T) { validator := NewValidator(addr1, pk1, Description{}) - abciVal := validator.ABCIValidator() + abciVal := validator.ABCIValidatorUpdate() require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) require.Equal(t, validator.BondedTokens().RoundInt64(), abciVal.Power) } -func TestABCIValidatorZero(t *testing.T) { +func TestABCIValidatorUpdateZero(t *testing.T) { validator := NewValidator(addr1, pk1, Description{}) - abciVal := validator.ABCIValidatorZero() + abciVal := validator.ABCIValidatorUpdateZero() require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) require.Equal(t, int64(0), abciVal.Power) } From a7fabbea167bcbb2002b95785f952efa7adb2265 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 3 Oct 2018 12:02:34 -0400 Subject: [PATCH 07/15] Merge PR #2431: Update some links --- README.md | 7 +++---- docs/README.md | 4 ++-- docs/getting-started/installation.md | 2 +- docs/getting-started/networks.md | 2 +- docs/introduction/tendermint.md | 2 +- docs/sdk/clients.md | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 75761f5f4..94b2b19da 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ breaking changes. ## Gaia Testnet To join the latest testnet, follow -[the guide](https://cosmos.network/docs/getting-started/full-node.html#setting-up-a-new-node). +[the guide](./docs/getting-started/join-testnet.md). For status updates and genesis files, see the [testnets repo](https://github.com/cosmos/testnets). @@ -30,15 +30,14 @@ For status updates and genesis files, see the ## Install See the -[install instructions](https://cosmos.network/docs/getting-started/installation.html). +[install instructions](./docs/getting-started/installation.md). ## Quick Start See the [Cosmos Docs](https://cosmos.network/docs/) -- [Getting started with the SDK](https://cosmos.network/docs/sdk/core/intro.html) +- [Getting started with the SDK](./docs/sdk/core/intro.md) - [SDK Examples](/examples) -- [Join the testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node) ## Disambiguation diff --git a/docs/README.md b/docs/README.md index 2edcebe8d..13b831f39 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,9 +10,9 @@ Cosmos can interoperate with multiple other applications and cryptocurrencies. B ## Quick Start -- [Getting started with the SDK](./sdk/core/intro.html) +- [Getting started with the SDK](./sdk/core/intro.md) - [SDK Examples](../examples) -- [Join the testnet](./getting-started/full-node.html#run-a-full-node) +- [Join the testnet](./getting-started/join-testnet.md#run-a-full-node) ## Edit the Documentation diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index c23f3b02c..66ccae4b8 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -1,6 +1,6 @@ # Install the SDK -This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](full-node.md) or a [Validator](/validators/validator-setup.md). +This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](./join-testnet.md#run-a-full-node) or a [Validator](/validators/validator-setup.md). ## Install Go diff --git a/docs/getting-started/networks.md b/docs/getting-started/networks.md index abd2999ba..67f0dab5a 100644 --- a/docs/getting-started/networks.md +++ b/docs/getting-started/networks.md @@ -9,7 +9,7 @@ From the [networks/local directory](https://github.com/cosmos/cosmos-sdk/tree/de ### Requirements -- [Install gaia](https://cosmos.network/docs/getting-started/installation.html) +- [Install gaia](./installation.md) - [Install docker](https://docs.docker.com/engine/installation/) - [Install docker-compose](https://docs.docker.com/compose/install/) diff --git a/docs/introduction/tendermint.md b/docs/introduction/tendermint.md index 62db8cdbc..4192a03ea 100644 --- a/docs/introduction/tendermint.md +++ b/docs/introduction/tendermint.md @@ -10,7 +10,7 @@ The ability to tolerate machines failing in arbitrary ways, including becoming m ## Application Blockchain Interface -Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html#abci-overview) for a deep dive into the ABCI. +Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.com/docs/introduction/introduction.html#abci-overview) for a deep dive into the ABCI. ## Understanding the roles of the different layers diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 5e5a9c7ac..9637b3b4b 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -169,7 +169,7 @@ gaiacli tx broadcast --node= signedSendTx.json #### Set up a Validator -Please refer to the [Validator Setup](https://cosmos.network/docs/validators/validator-setup.html) section for a more complete guide on how to set up a validator-candidate. +Please refer to the [Validator Setup](../validators/validator-setup.md) section for a more complete guide on how to set up a validator-candidate. #### Delegate to a Validator From 324bdaf55ded49e792d2755cf275c1909494fd7a Mon Sep 17 00:00:00 2001 From: Rigel Date: Wed, 3 Oct 2018 12:37:06 -0400 Subject: [PATCH 08/15] Merge PR #2394: Split up UpdateValidator into distinct state transitions applied only in EndBlock --- Gopkg.lock | 16 +- PENDING.md | 1 + x/gov/tally_test.go | 16 + x/mock/simulation/random_simulate_blocks.go | 17 +- x/slashing/handler_test.go | 3 + x/slashing/keeper_test.go | 27 +- x/slashing/tick_test.go | 3 + x/stake/genesis.go | 16 +- x/stake/handler.go | 10 +- x/stake/handler_test.go | 200 ++---- x/stake/keeper/_store.md | 6 - x/stake/keeper/delegation.go | 30 +- x/stake/keeper/delegation_test.go | 74 ++- x/stake/keeper/keeper.go | 16 - x/stake/keeper/key.go | 47 +- x/stake/keeper/slash.go | 29 +- x/stake/keeper/slash_test.go | 21 +- x/stake/keeper/test_common.go | 21 +- x/stake/keeper/val_state_change.go | 268 ++++++++ x/stake/keeper/validator.go | 684 ++++---------------- x/stake/keeper/validator_test.go | 452 ++++++------- x/stake/querier/queryable_test.go | 9 +- x/stake/stake.go | 5 - x/stake/types/validator.go | 7 + 24 files changed, 855 insertions(+), 1123 deletions(-) create mode 100644 x/stake/keeper/val_state_change.go diff --git a/Gopkg.lock b/Gopkg.lock index 5160d752e..62f1f7e5b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -239,12 +239,12 @@ version = "v1.0.0" [[projects]] - digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4" + digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3" name = "github.com/mitchellh/mapstructure" packages = ["."] pruneopts = "UT" - revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8" - version = "v1.0.0" + revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad" + version = "v1.1.1" [[projects]] digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" @@ -536,7 +536,7 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" + revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900" [[projects]] digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" @@ -556,14 +556,14 @@ [[projects]] branch = "master" - digest = "1:68023dc297a659d5eb2dafd62eda811456b338c5b3ec3c27da79e8a47d3f456a" + digest = "1:8bc8ecef1d63576cfab4d08b44a1f255dd67e5b019b7a44837d62380f266a91c" name = "golang.org/x/sys" packages = [ "cpu", "unix", ] pruneopts = "UT" - revision = "2f1df4e56cdeb503a08d8577e6f1a7eb12efab82" + revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7" [[projects]] digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" @@ -590,11 +590,11 @@ [[projects]] branch = "master" - digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" + digest = "1:1e6b0176e8c5dd8ff551af65c76f8b73a99bcf4d812cedff1b91711b7df4804c" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "UT" - revision = "0e822944c569bf5c9afd034adaa56208bd2906ac" + revision = "c7e5094acea1ca1b899e2259d80a6b0f882f81f8" [[projects]] digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" diff --git a/PENDING.md b/PENDING.md index 05c677265..18da22592 100644 --- a/PENDING.md +++ b/PENDING.md @@ -64,6 +64,7 @@ BREAKING CHANGES * [x/auth] \#2377 auth.StdSignMsg -> txbuilder.StdSignMsg * [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index * [x/staking] \#2236 more distribution hooks for distribution + * [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock * Tendermint diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 28fe0cb59..26b32cc93 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -45,6 +45,7 @@ func TestTallyNoOneVotes(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -69,6 +70,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -98,6 +100,7 @@ func TestTallyOnlyValidators51No(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -126,6 +129,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -157,6 +161,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -188,6 +193,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -219,6 +225,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -250,6 +257,7 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7}) + stake.EndBlocker(ctx, sk) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() @@ -279,6 +287,7 @@ func TestTallyDelgatorOverride(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) + stake.EndBlocker(ctx, sk) delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30)) stakeHandler(ctx, delegator1Msg) @@ -315,6 +324,7 @@ func TestTallyDelgatorInherit(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) + stake.EndBlocker(ctx, sk) delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30)) stakeHandler(ctx, delegator1Msg) @@ -349,6 +359,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7}) + stake.EndBlocker(ctx, sk) delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10)) stakeHandler(ctx, delegator1Msg) @@ -402,6 +413,8 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10)) stakeHandler(ctx, delegator1Msg2) + stake.EndBlocker(ctx, sk) + proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() proposal.SetStatus(StatusVotingPeriod) @@ -432,6 +445,7 @@ func TestTallyJailedValidator(t *testing.T) { } createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7}) + stake.EndBlocker(ctx, sk) delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10)) stakeHandler(ctx, delegator1Msg) @@ -443,6 +457,8 @@ func TestTallyJailedValidator(t *testing.T) { require.True(t, found) sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address())) + stake.EndBlocker(ctx, sk) + proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() proposal.SetStatus(StatusVotingPeriod) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index ee93cfea3..51705cbef 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -36,12 +36,8 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { - pubkey, err := tmtypes.PB2TM.PubKey(validator.PubKey) - if err != nil { - panic(err) - } - address := pubkey.Address() - validators[string(address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} + str := fmt.Sprintf("%v", validator.PubKey) + validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} } for i := 0; i < len(setups); i++ { @@ -400,22 +396,23 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { for _, update := range updates { + str := fmt.Sprintf("%v", update.PubKey) switch { case update.Power == 0: - if _, ok := current[string(update.PubKey.Data)]; !ok { + if _, ok := current[str]; !ok { tb.Fatalf("tried to delete a nonexistent validator") } event("endblock/validatorupdates/kicked") - delete(current, string(update.PubKey.Data)) + delete(current, str) default: // Does validator already exist? - if mVal, ok := current[string(update.PubKey.Data)]; ok { + if mVal, ok := current[str]; ok { mVal.val = update event("endblock/validatorupdates/updated") } else { // Set this new validator - current[string(update.PubKey.Data)] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)} + current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)} event("endblock/validatorupdates/added") } } diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 9cdcd0533..7aebf0d0b 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -45,6 +45,9 @@ func TestJailedValidatorDelegations(t *testing.T) { got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal) require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got) + // end block + stake.EndBlocker(ctx, stakeKeeper) + // set dummy signing info newInfo := ValidatorSigningInfo{ StartHeight: int64(0), diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 6845f35ce..e32e7af5b 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -85,13 +85,16 @@ func TestSlashingPeriodCap(t *testing.T) { // double sign less than max age keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt) - // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) + // end block + stake.EndBlocker(ctx, sk) // update block height ctx = ctx.WithBlockHeight(int64(1)) // unjail to measure power sk.Unjail(ctx, valConsAddr) + // end block + stake.EndBlocker(ctx, sk) // power should be reduced expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) @@ -100,10 +103,14 @@ func TestSlashingPeriodCap(t *testing.T) { keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt) // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) + // end block + stake.EndBlocker(ctx, sk) // update block height ctx = ctx.WithBlockHeight(int64(2)) // unjail to measure power sk.Unjail(ctx, valConsAddr) + // end block + stake.EndBlocker(ctx, sk) // power should be equal, no more should have been slashed expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) @@ -114,6 +121,8 @@ func TestSlashingPeriodCap(t *testing.T) { require.True(t, sk.Validator(ctx, addr).GetJailed()) // unjail to measure power sk.Unjail(ctx, valConsAddr) + // end block + stake.EndBlocker(ctx, sk) // power should be reduced expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) @@ -180,6 +189,9 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, int64(0), info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) + // end block + stake.EndBlocker(ctx, sk) + // validator should have been jailed validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) @@ -193,6 +205,9 @@ func TestHandleAbsentValidator(t *testing.T) { got = slh(ctx, NewMsgUnjail(addr)) require.True(t, got.IsOK()) + // end block + stake.EndBlocker(ctx, sk) + // validator should be rebonded now validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) @@ -222,12 +237,19 @@ func TestHandleAbsentValidator(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } + // end block + stake.EndBlocker(ctx, sk) + // validator should be jailed again after 500 unsigned blocks nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } + + // end block + stake.EndBlocker(ctx, sk) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) } @@ -296,6 +318,9 @@ func TestHandleAlreadyJailed(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } + // end block + stake.EndBlocker(ctx, sk) + // validator should have been jailed and slashed validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 5f0ccf8ed..a98d97c54 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -77,6 +77,9 @@ func TestBeginBlocker(t *testing.T) { BeginBlocker(ctx, req, keeper) } + // end block + stake.EndBlocker(ctx, sk) + // validator should be jailed validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.True(t, found) diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 740eba415..e1f33d94c 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -17,7 +17,7 @@ import ( // Returns final validator set after applying all declaration and delegations func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) { keeper.SetPool(ctx, data.Pool) - keeper.SetNewParams(ctx, data.Params) + keeper.SetParams(ctx, data.Params) keeper.InitIntraTxCounter(ctx) for i, validator := range data.Validators { @@ -31,26 +31,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator) } - // Manually set indexes for the first time + // Manually set indices for the first time keeper.SetValidatorByConsAddr(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool) - - if validator.Status == sdk.Bonded { - keeper.SetValidatorBondedIndex(ctx, validator) - } } for _, bond := range data.Bonds { keeper.SetDelegation(ctx, bond) } - keeper.UpdateBondedValidatorsFull(ctx) - - vals := keeper.GetValidatorsBonded(ctx) - res = make([]abci.ValidatorUpdate, len(vals)) - for i, val := range vals { - res[i] = val.ABCIValidatorUpdate() - } + res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) return } diff --git a/x/stake/handler.go b/x/stake/handler.go index 65915a65e..f3a007fd5 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid k.SetIntraTxCounter(ctx, 0) // calculate validator set changes - ValidatorUpdates = k.GetTendermintUpdates(ctx) + ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) return } @@ -82,7 +82,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k msg.Commission.Rate, msg.Commission.MaxChangeRate, msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, ) - validator, err := validator.SetInitialCommission(commission) if err != nil { return err.Result() @@ -90,6 +89,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k k.SetValidator(ctx, validator) k.SetValidatorByConsAddr(ctx, validator) + k.SetNewValidatorByPowerIndex(ctx, validator) // move coins from the msg.Address account to a (self-delegation) delegator account // the validator account and global shares are updated within here @@ -130,13 +130,13 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe validator.Description = description if msg.CommissionRate != nil { - if err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate); err != nil { + commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) + if err != nil { return err.Result() } + validator.Commission = commission } - // We don't need to run through all the power update logic within k.UpdateValidator - // We just need to override the entry in state, since only the description has changed. k.SetValidator(ctx, validator) tags := sdk.NewTags( diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 5780bf543..0e4a2bf65 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -63,6 +63,10 @@ func TestValidatorByPowerIndex(t *testing.T) { got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + // verify the self-delegation exists bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) @@ -83,14 +87,20 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + // must end-block + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + // slash and jail the first validator consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1)) keeper.Jail(ctx, consAddr0) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed + keeper.Unjail(ctx, consAddr0) // the old power record should have been deleted as the power changed require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) @@ -121,6 +131,8 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) + EndBlocker(ctx, keeper) + // verify that by power key nolonger exists _, found = keeper.GetValidator(ctx, validatorAddr) require.False(t, found) @@ -136,8 +148,10 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10) got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper) require.True(t, got.IsOK(), "%v", got) - validator, found := keeper.GetValidator(ctx, addr1) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + validator, found := keeper.GetValidator(ctx, addr1) require.True(t, found) assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr1, validator.OperatorAddr) @@ -160,6 +174,11 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10) got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper) require.True(t, got.IsOK(), "%v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found = keeper.GetValidator(ctx, addr2) require.True(t, found) @@ -180,6 +199,11 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10) got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) require.True(t, got.IsOK(), "%v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -211,6 +235,10 @@ func TestLegacyValidatorDelegations(t *testing.T) { got := handleMsgCreateValidator(ctx, msgCreateVal, keeper) require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got) + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + // verify the validator exists and has the correct attributes validator, found := keeper.GetValidator(ctx, valAddr) require.True(t, found) @@ -302,6 +330,9 @@ func TestIncrementsMsgDelegate(t *testing.T) { got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) @@ -384,6 +415,9 @@ func TestIncrementsMsgUnbond(t *testing.T) { amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64()) @@ -533,11 +567,8 @@ func TestMultipleMsgDelegate(t *testing.T) { // unbond them all for i, delegatorAddr := range delegatorAddrs { msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) - msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) - got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) - require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) //Check that the account is unbonded _, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) @@ -564,11 +595,8 @@ func TestJailValidator(t *testing.T) { // unbond the validators bond portion msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10)) - msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(sdk.AccAddress(validatorAddr), validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) - require.True(t, got.IsOK(), "expected no error") - got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper) - require.True(t, got.IsOK(), "expected no error") + require.True(t, got.IsOK(), "expected no error: %v", got) validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -750,16 +778,22 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx))) msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) // unbond the valdator-2 @@ -767,6 +801,9 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + // because there are extra validators waiting to get in, the queued // validator (aka. validator-1) should make it into the bonded group, thus // the total number of validators should stay the same @@ -777,142 +814,6 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { require.Equal(t, sdk.Bonded, val1.Status, "%v", val1) } -func TestJoiningAsCliffValidator(t *testing.T) { - ctx, _, keeper := keep.CreateTestInput(t, false, 1000) - validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) - - // make sure that the cliff validator is nil to begin with - cliffVal := keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // set the unbonding time - params := keeper.GetParams(ctx) - params.UnbondingTime = 0 - params.MaxValidators = 2 - keeper.SetParams(ctx, params) - - // add the first validator - msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50) - got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // cliff validator should still be nil - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // Add the second validator - msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30) - got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // now that we've reached maximum validators, the val-2 should be added to the cliff (top) - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, validatorAddr2.Bytes(), cliffVal) -} - -func TestJoiningToCreateFirstCliffValidator(t *testing.T) { - ctx, _, keeper := keep.CreateTestInput(t, false, 1000) - validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) - - // make sure that the cliff validator is nil to begin with - cliffVal := keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // set the unbonding time - params := keeper.GetParams(ctx) - params.UnbondingTime = 0 - params.MaxValidators = 2 - keeper.SetParams(ctx, params) - - // add the first validator - msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50) - got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // cliff validator should still be nil - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // Add the second validator - msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 60) - got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // now that we've reached maximum validators, validator-1 should be added to the cliff (top) - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, validatorAddr1.Bytes(), cliffVal) -} - -func TestCliffValidator(t *testing.T) { - ctx, _, keeper := keep.CreateTestInput(t, false, 1000) - validatorAddr1 := sdk.ValAddress(keep.Addrs[0]) - validatorAddr2 := sdk.ValAddress(keep.Addrs[1]) - validatorAddr3 := sdk.ValAddress(keep.Addrs[2]) - - // make sure that the cliff validator is nil to begin with - cliffVal := keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // set the unbonding time - params := keeper.GetParams(ctx) - params.UnbondingTime = 0 - params.MaxValidators = 2 - keeper.SetParams(ctx, params) - - // add the first validator - msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50) - got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // cliff validator should still be nil - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) - - // Add the second validator - msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30) - got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - // now that we've reached maximum validators, validator-2 should be added to the cliff (top) - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, validatorAddr2.Bytes(), cliffVal) - - // add the third validator, which should not make it to being bonded, - // so the cliff validator should not change because nobody has been kicked out - msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10) - got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, validatorAddr2.Bytes(), cliffVal) - - // unbond valdator-2 - msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30)) - got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") - - vals := keeper.GetValidatorsBonded(ctx) - require.Equal(t, 2, len(vals)) - - // now the validator set should be updated, - // where val-3 enters the validator set on the cliff - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, validatorAddr3.Bytes(), cliffVal) - - // unbond valdator-1 - msgBeginUnbonding = NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr1), validatorAddr1, sdk.NewDec(50)) - got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) - require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") - - // get bonded validators - should just be one - vals = keeper.GetValidatorsBonded(ctx) - require.Equal(t, 1, len(vals)) - - // cliff now should be empty - cliffVal = keeper.GetCliffValidator(ctx) - require.Equal(t, []byte(nil), cliffVal) -} - func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] @@ -931,6 +832,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { got = handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgDelegate") + // apply Tendermint updates + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + // a block passes ctx = ctx.WithBlockHeight(1) @@ -949,6 +854,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { require.True(t, found) require.Equal(t, sdk.NewDec(6), delegation.Shares) + // must apply validator updates + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + // slash the validator by half keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) @@ -991,6 +900,9 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { require.True(t, found) require.Equal(t, sdk.NewDec(3), delegation.Shares) + // end blocker + EndBlocker(ctx, keeper) + // validator power should have been reduced to zero // ergo validator should have been removed from the store _, found = keeper.GetValidator(ctx, valA) diff --git a/x/stake/keeper/_store.md b/x/stake/keeper/_store.md index 5c070b9e0..d56938941 100644 --- a/x/stake/keeper/_store.md +++ b/x/stake/keeper/_store.md @@ -41,9 +41,3 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`. The transient store persists between transations but not between blocks -## Tendermint Updates - - 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 diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index cc46646a7..d7e9c5795 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -265,18 +265,13 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co } } - pool := k.GetPool(ctx) - validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount) + validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt.Amount) + + // Update delegation delegation.Shares = delegation.Shares.Add(newShares) - - // Update delegation height delegation.Height = ctx.BlockHeight() - - k.SetPool(ctx, pool) k.SetDelegation(ctx, delegation) - k.UpdateValidator(ctx, validator) - - return + return newShares, nil } // unbond the the delegation return @@ -313,8 +308,9 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA // if the delegation is the operator of the validator then // trigger a jail validator - if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && validator.Jailed == false { - validator.Jailed = true + if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && !validator.Jailed { + k.jailValidator(ctx, validator) + validator = k.mustGetValidator(ctx, validator.OperatorAddr) } k.RemoveDelegation(ctx, delegation) @@ -325,14 +321,10 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA } // remove the coins from the validator - pool := k.GetPool(ctx) - validator, pool, amount = validator.RemoveDelShares(pool, shares) + validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares) - k.SetPool(ctx, pool) - - // update then remove validator if necessary - validator = k.UpdateValidator(ctx, validator) - if validator.DelegatorShares.IsZero() { + if validator.DelegatorShares.IsZero() && validator.Status != sdk.Bonded { + // if bonded, we must remove in EndBlocker instead k.RemoveValidator(ctx, validator.OperatorAddr) } @@ -368,7 +360,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd } } -// complete unbonding an unbonding record +// begin unbonding an unbonding record func (k Keeper) BeginUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error { diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index ed65f1f40..863c08a96 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -25,9 +25,9 @@ func TestDelegation(t *testing.T) { } keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - validators[2] = keeper.UpdateValidator(ctx, validators[2]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + validators[2] = testingUpdateValidator(keeper, ctx, validators[2]) // first add a validators[0] to delegate too @@ -184,7 +184,7 @@ func TestUnbondDelegation(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) require.Equal(t, int64(10), pool.BondedTokens.RoundInt64()) @@ -226,7 +226,7 @@ func TestUndelegateSelfDelegation(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) selfDelegation := types.Delegation{ DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()), @@ -236,10 +236,11 @@ func TestUndelegateSelfDelegation(t *testing.T) { keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -252,6 +253,10 @@ func TestUndelegateSelfDelegation(t *testing.T) { err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, int64(10), validator.Tokens.RoundInt64()) @@ -269,7 +274,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) selfDelegation := types.Delegation{ DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()), @@ -279,10 +284,11 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -303,6 +309,10 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) @@ -340,7 +350,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.Delegation{ @@ -351,10 +361,11 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -374,6 +385,10 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) @@ -506,7 +521,7 @@ func TestRedelegateSelfDelegation(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.Delegation{ @@ -520,14 +535,16 @@ func TestRedelegateSelfDelegation(t *testing.T) { validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) + pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(10)) keeper.SetPool(ctx, pool) - validator2 = keeper.UpdateValidator(ctx, validator2) + validator2 = testingUpdateValidator(keeper, ctx, validator2) + require.Equal(t, sdk.Bonded, validator2.Status) // create a second delegation to this validator validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -539,6 +556,10 @@ func TestRedelegateSelfDelegation(t *testing.T) { err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, int64(10), validator.Tokens.RoundInt64()) @@ -552,11 +573,12 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator.BondIntraTxCounter = 1 validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.Delegation{ @@ -567,10 +589,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -581,10 +604,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2.BondIntraTxCounter = 2 validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator2 = keeper.UpdateValidator(ctx, validator2) + validator2 = testingUpdateValidator(keeper, ctx, validator2) header := ctx.BlockHeader() blockHeight := int64(10) @@ -597,6 +621,10 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) @@ -634,7 +662,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.Delegation{ @@ -645,10 +673,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) + validator.BondIntraTxCounter = 1 require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) pool = keeper.GetPool(ctx) delegation := types.Delegation{ DelegatorAddr: addrDels[0], @@ -659,10 +689,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2.BondIntraTxCounter = 2 validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) require.Equal(t, int64(10), issuedShares.RoundInt64()) keeper.SetPool(ctx, pool) - validator2 = keeper.UpdateValidator(ctx, validator2) + validator2 = testingUpdateValidator(keeper, ctx, validator2) + require.Equal(t, sdk.Bonded, validator2.Status) header := ctx.BlockHeader() blockHeight := int64(10) @@ -675,6 +707,10 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) require.NoError(t, err) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 82170a4ae..007c2a5de 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -64,25 +64,9 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { return } -// Need a distinct function because setParams depends on an existing previous -// record of params to exist (to check if maxValidators has changed) - and we -// panic on retrieval if it doesn't exist - hence if we use setParams for the very -// first params set it will panic. -func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) { - store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(params) - store.Set(ParamKey, b) -} - // set the params func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { store := ctx.KVStore(k.storeKey) - exParams := k.GetParams(ctx) - - // if max validator count changes, must recalculate validator set - if exParams.MaxValidators != params.MaxValidators { - k.UpdateBondedValidatorsFull(ctx) - } b := k.cdc.MustMarshalBinary(params) store.Set(ParamKey, b) } diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 91e6a6970..02d54e2b7 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -18,18 +18,13 @@ var ( ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators 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 - 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 + IntraTxCounterKey = []byte{0x06} // key for intra-block tx index + DelegationKey = []byte{0x07} // key for a delegation + UnbondingDelegationKey = []byte{0x08} // key for an unbonding-delegation + UnbondingDelegationByValIndexKey = []byte{0x09} // prefix for each key for an unbonding-delegation, by validator operator + RedelegationKey = []byte{0x0A} // key for a redelegation + RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator ) const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch @@ -46,12 +41,6 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { return append(ValidatorsByConsAddrKey, addr.Bytes()...) } -// gets the key for the current validator group -// VALUE: none (key rearrangement with GetValKeyFromValBondedIndexKey) -func GetValidatorsBondedIndexKey(operatorAddr sdk.ValAddress) []byte { - return append(ValidatorsBondedIndexKey, operatorAddr.Bytes()...) -} - // Get the validator operator address from ValBondedIndexKey func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte { return IndexKey[1:] // remove prefix bytes @@ -66,6 +55,11 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) [] return getValidatorPowerRank(validator, pool) } +// get the bonded validator index key for an operator address +func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte { + return append(ValidatorsBondedIndexKey, operator...) +} + // get the power ranking of a validator // NOTE the larger values are of higher value // nolint: unparam @@ -74,34 +68,19 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { potentialPower := validator.Tokens powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first) - jailedBytes := make([]byte, 1) - if validator.Jailed { - jailedBytes[0] = byte(0x00) - } else { - jailedBytes[0] = byte(0x01) - } - // heightBytes and counterBytes represent strings like powerBytes does heightBytes := make([]byte, binary.MaxVarintLen64) binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first) counterBytes := make([]byte, 2) binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority) - return append(append(append(append( + return append(append(append( ValidatorsByPowerIndexKey, - jailedBytes...), powerBytes...), heightBytes...), counterBytes...) } -// get the key for the accumulated update validators -// VALUE: abci.Validator -// note records using these keys should never persist between blocks -func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte { - return append(TendermintUpdatesTKey, operatorAddr.Bytes()...) -} - //______________________________________________________________________________ // gets the key for delegator bond with validator diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 7bfe4eb25..dc57dc5dd 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -95,20 +95,18 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh } } - // Cannot decrease balance below zero + // cannot decrease balance below zero tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens) - // burn validator's tokens + // burn validator's tokens and update the validator + validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn) pool := k.GetPool(ctx) - validator, pool = validator.RemoveTokens(pool, tokensToBurn) pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) k.SetPool(ctx, pool) - // update the validator, possibly kicking it out - validator = k.UpdateValidator(ctx, validator) - // remove validator if it has no more tokens - if validator.Tokens.IsZero() { + if validator.Tokens.IsZero() && validator.Status != sdk.Bonded { + // if bonded, we must remove in ApplyAndReturnValidatorSetUpdates instead k.RemoveValidator(ctx, validator.OperatorAddr) } @@ -123,7 +121,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh // jail a validator func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { - k.setJailed(ctx, consAddr, true) + validator := k.mustGetValidatorByConsAddr(ctx, consAddr) + k.jailValidator(ctx, validator) logger := ctx.Logger().With("module", "x/stake") logger.Info(fmt.Sprintf("validator %s jailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 @@ -132,24 +131,14 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { // unjail a validator func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { - k.setJailed(ctx, consAddr, false) + validator := k.mustGetValidatorByConsAddr(ctx, consAddr) + k.unjailValidator(ctx, validator) logger := ctx.Logger().With("module", "x/stake") logger.Info(fmt.Sprintf("validator %s unjailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } -// set the jailed flag on a validator -func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) { - validator, found := k.GetValidatorByConsAddr(ctx, consAddr) - if !found { - panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed)) - } - validator.Jailed = isJailed - k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it - return -} - // slash an unbonding delegation and update the pool // return the amount that would have been slashed assuming // the unbonding delegation had enough stake to slash diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 0a6cccac2..7959679c6 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -26,8 +26,10 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { for i := 0; i < numVals; i++ { validator := types.NewValidator(addrVals[i], PKs[i], types.Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt)) + validator.BondIntraTxCounter = int16(i) + pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(amt)) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) keeper.SetValidatorByConsAddr(ctx, validator) } pool = keeper.GetPool(ctx) @@ -161,6 +163,10 @@ func TestSlashRedelegation(t *testing.T) { rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + // initialbalance unchanged require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance) @@ -201,6 +207,11 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) { require.True(t, found) newPool := keeper.GetPool(ctx) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates) + + validator = keeper.mustGetValidator(ctx, validator.OperatorAddr) // power decreased require.Equal(t, sdk.NewDec(5), validator.GetPower()) // pool bonded shares decreased @@ -232,6 +243,10 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { require.True(t, found) keeper.Slash(ctx, consAddr, 10, 10, fraction) + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + // read updating unbonding delegation ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) @@ -301,6 +316,8 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { newPool = keeper.GetPool(ctx) // just 1 bonded token burned again since that's all the validator now has require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator // power decreased by 1 again, validator is out of stake // ergo validator should have been removed from the store @@ -402,6 +419,8 @@ func TestSlashWithRedelegation(t *testing.T) { newPool = keeper.GetPool(ctx) // four more bonded tokens burned require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator // validator decreased to zero power, should have been removed from the store _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 03d4642b6..8f5b2fa39 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -111,7 +111,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context ck := bank.NewBaseKeeper(accountMapper) keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace) keeper.SetPool(ctx, types.InitialPool()) - keeper.SetNewParams(ctx, types.DefaultParams()) + keeper.SetParams(ctx, types.DefaultParams()) keeper.InitIntraTxCounter(ctx) // fill all the addresses with some coins, set the loose pool tokens simultaneously @@ -202,5 +202,22 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey { // does a certain by-power index record exist func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool { store := ctx.KVStore(keeper.storeKey) - return store.Get(power) != nil + return store.Has(power) +} + +func testingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator { + pool := keeper.GetPool(ctx) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator, pool) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, found := keeper.GetValidator(ctx, validator.OperatorAddr) + if !found { + panic("validator expected but not found") + } + return validator +} + +func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(power) } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go new file mode 100644 index 000000000..f9cf6e463 --- /dev/null +++ b/x/stake/keeper/val_state_change.go @@ -0,0 +1,268 @@ +package keeper + +import ( + "bytes" + "fmt" + "sort" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" +) + +// Apply and return accumulated updates to the bonded validator set +// +// 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) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) { + + store := ctx.KVStore(k.storeKey) + maxValidators := k.GetParams(ctx).MaxValidators + + // retrieve last validator set + last := k.retrieveLastValidatorSet(ctx) + + // iterate over validators, highest power to lowest + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + count := 0 + for ; iterator.Valid() && count < int(maxValidators); iterator.Next() { + + // fetch the validator + operator := sdk.ValAddress(iterator.Value()) + validator := k.mustGetValidator(ctx, operator) + + if validator.Jailed { + panic("should never retrieve a jailed validator from the power store") + } + + // if we get to a zero-power validator (which we don't bond), + // there are no more possible bonded validators + // note: we must check the ABCI power, since we round before sending to Tendermint + if validator.Tokens.RoundInt64() == int64(0) { + break + } + + // apply the appropriate state change if necessary + switch validator.Status { + case sdk.Unbonded: + validator = k.unbondedToBonded(ctx, validator) + case sdk.Unbonding: + validator = k.unbondingToBonded(ctx, validator) + case sdk.Bonded: + // no state change + default: + panic("unexpected validator status") + } + + // fetch the old power bytes + var operatorBytes [sdk.AddrLen]byte + copy(operatorBytes[:], operator[:]) + oldPowerBytes, found := last[operatorBytes] + + // calculate the new power bytes + newPowerBytes := validator.ABCIValidatorPowerBytes(k.cdc) + + // update the validator set if power has changed + if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { + updates = append(updates, validator.ABCIValidatorUpdate()) + } + + // validator still in the validator set, so delete from the copy + delete(last, operatorBytes) + + // set the bonded validator index + store.Set(GetBondedValidatorIndexKey(operator), newPowerBytes) + + // keep count + count++ + + } + + // sort the no-longer-bonded validators + noLongerBonded := k.sortNoLongerBonded(last) + + // iterate through the sorted no-longer-bonded validators + for _, operator := range noLongerBonded { + + // fetch the validator + validator := k.mustGetValidator(ctx, sdk.ValAddress(operator)) + + // bonded to unbonding + k.bondedToUnbonding(ctx, validator) + + // remove validator if it has no more tokens + if validator.Tokens.IsZero() { + k.RemoveValidator(ctx, validator.OperatorAddr) + } + + // delete from the bonded validator index + store.Delete(GetBondedValidatorIndexKey(operator)) + + // update the validator set + updates = append(updates, validator.ABCIValidatorUpdateZero()) + + } + + return updates +} + +// Validator state transitions + +func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator { + if validator.Status != sdk.Bonded { + panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator)) + } + return k.beginUnbondingValidator(ctx, validator) +} + +func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator { + if validator.Status != sdk.Unbonding { + panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) + } + return k.bondValidator(ctx, validator) +} + +func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator { + if validator.Status != sdk.Unbonded { + panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator)) + } + return k.bondValidator(ctx, validator) +} + +func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator { + if validator.Status != sdk.Unbonded { + panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) + } + return k.completeUnbondingValidator(ctx, validator) +} + +// send a validator to jail +func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) { + if validator.Jailed { + panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator)) + } + + pool := k.GetPool(ctx) + validator.Jailed = true + k.SetValidator(ctx, validator) + k.DeleteValidatorByPowerIndex(ctx, validator, pool) +} + +// remove a validator from jail +func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) { + if !validator.Jailed { + panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator)) + } + + pool := k.GetPool(ctx) + validator.Jailed = false + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator, pool) +} + +// perform all the store operations for when a validator status becomes bonded +func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator { + + pool := k.GetPool(ctx) + + k.DeleteValidatorByPowerIndex(ctx, validator, pool) + + validator.BondHeight = ctx.BlockHeight() + + // set the status + validator, pool = validator.UpdateStatus(pool, sdk.Bonded) + k.SetPool(ctx, pool) + + // save the now bonded validator record to the three referenced stores + k.SetValidator(ctx, validator) + + k.SetValidatorByPowerIndex(ctx, validator, pool) + + // call the bond hook if present + if k.hooks != nil { + k.hooks.OnValidatorBonded(ctx, validator.ConsAddress()) + } + + return validator +} + +// perform all the store operations for when a validator status begins unbonding +func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { + + pool := k.GetPool(ctx) + params := k.GetParams(ctx) + + k.DeleteValidatorByPowerIndex(ctx, validator, pool) + + // sanity check + if validator.Status != sdk.Bonded { + panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator)) + } + + // set the status + validator, pool = validator.UpdateStatus(pool, sdk.Unbonding) + k.SetPool(ctx, pool) + + validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) + validator.UnbondingHeight = ctx.BlockHeader().Height + + // save the now unbonded validator record + k.SetValidator(ctx, validator) + + k.SetValidatorByPowerIndex(ctx, validator, pool) + + // call the unbond hook if present + if k.hooks != nil { + k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) + } + + return validator +} + +// perform all the store operations for when a validator status becomes unbonded +func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { + pool := k.GetPool(ctx) + validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) + k.SetPool(ctx, pool) + k.SetValidator(ctx, validator) + return validator +} + +// map of operator addresses to serialized power +type validatorsByAddr map[[sdk.AddrLen]byte][]byte + +// retrieve the last validator set +func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr { + last := make(validatorsByAddr) + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) + for ; iterator.Valid(); iterator.Next() { + var operator [sdk.AddrLen]byte + copy(operator[:], iterator.Key()[1:]) + powerBytes := iterator.Value() + last[operator] = make([]byte, len(powerBytes)) + copy(last[operator][:], powerBytes[:]) + } + return last +} + +// given a map of remaining validators to previous bonded power +// returns the list of validators to be unbonded, sorted by operator address +func (k Keeper) sortNoLongerBonded(last validatorsByAddr) [][]byte { + // sort the map keys for determinism + noLongerBonded := make([][]byte, len(last)) + index := 0 + for operatorBytes := range last { + operator := make([]byte, sdk.AddrLen) + copy(operator[:], operatorBytes[:]) + noLongerBonded[index] = operator + index++ + } + // sorted by address - order doesn't matter + sort.SliceStable(noLongerBonded, func(i, j int) bool { + return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1 + }) + return noLongerBonded +} diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 668a2af23..b405312a1 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -1,13 +1,9 @@ package keeper import ( - "bytes" "container/list" "fmt" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -58,6 +54,14 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty return validator, true } +func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Validator { + validator, found := k.GetValidator(ctx, addr) + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", addr)) + } + return validator +} + // get a single validator by consensus address func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) @@ -68,6 +72,16 @@ func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress return k.GetValidator(ctx, opAddr) } +func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) types.Validator { + validator, found := k.GetValidatorByConsAddr(ctx, consAddr) + if !found { + panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr)) + } + return validator +} + +//___________________________________________________________________________ + // set the main record holding validator details func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) @@ -84,23 +98,110 @@ func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validato // validator index func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) { + // jailed validators are not kept in the power index + if validator.Jailed { + return + } store := ctx.KVStore(k.storeKey) store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr) } // validator index -func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) { +func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) { store := ctx.KVStore(k.storeKey) - store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{}) + store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) } -// used in testing -func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool { +// validator index +func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) - return store.Get(power) != nil + pool := k.GetPool(ctx) + store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr) } -// Get the set of all validators with no limits, used during genesis dump +//___________________________________________________________________________ + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, + tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) { + + pool := k.GetPool(ctx) + k.DeleteValidatorByPowerIndex(ctx, validator, pool) + validator, pool, addedShares = validator.AddTokensFromDel(pool, tokensToAdd) + // increment the intra-tx counter + // in case of a conflict, the validator which least recently changed power takes precedence + counter := k.GetIntraTxCounter(ctx) + validator.BondIntraTxCounter = counter + k.SetIntraTxCounter(ctx, counter+1) + k.SetValidator(ctx, validator) + k.SetPool(ctx, pool) + k.SetValidatorByPowerIndex(ctx, validator, pool) + return validator, addedShares +} + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, + sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Dec) { + + pool := k.GetPool(ctx) + k.DeleteValidatorByPowerIndex(ctx, validator, pool) + validator, pool, removedTokens = validator.RemoveDelShares(pool, sharesToRemove) + k.SetValidator(ctx, validator) + k.SetPool(ctx, pool) + k.SetValidatorByPowerIndex(ctx, validator, pool) + return validator, removedTokens +} + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator { + pool := k.GetPool(ctx) + k.DeleteValidatorByPowerIndex(ctx, validator, pool) + validator, pool = validator.RemoveTokens(pool, tokensToRemove) + k.SetValidator(ctx, validator) + k.SetPool(ctx, pool) + k.SetValidatorByPowerIndex(ctx, validator, pool) + return validator +} + +// UpdateValidatorCommission attempts to update a validator's commission rate. +// An error is returned if the new commission rate is invalid. +func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) (types.Commission, sdk.Error) { + commission := validator.Commission + blockTime := ctx.BlockHeader().Time + + if err := commission.ValidateNewRate(newRate, blockTime); err != nil { + return commission, err + } + + commission.Rate = newRate + commission.UpdateTime = blockTime + + return commission, nil +} + +// remove the validator record and associated indexes +// except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates +func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { + + // first retrieve the old validator record + validator, found := k.GetValidator(ctx, address) + if !found { + return + } + + // delete the old validator record + store := ctx.KVStore(k.storeKey) + pool := k.GetPool(ctx) + store.Delete(GetValidatorKey(address)) + store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) + store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) + +} + +//___________________________________________________________________________ +// get groups of validators + +// get the set of all validators with no limits, used during genesis dump func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) @@ -132,8 +233,6 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [ return validators[:i] // trim if the array length < maxRetrieve } -//___________________________________________________________________________ - // get the group of the bonded validators func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) @@ -153,8 +252,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated") } address := GetAddressFromValBondedIndexKey(iterator.Key()) - validator, found := k.GetValidator(ctx, address) - ensureValidatorFound(found, address) + validator := k.mustGetValidator(ctx, address) validators[i] = validator i++ @@ -163,9 +261,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat } // get the group of bonded validators sorted by power-rank -// -// TODO: Rename to GetBondedValidatorsByPower or GetValidatorsByPower(ctx, status) -func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { +func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) maxValidators := k.GetParams(ctx).MaxValidators validators := make([]types.Validator, maxValidators) @@ -176,8 +272,7 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { i := 0 for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { address := iterator.Value() - validator, found := k.GetValidator(ctx, address) - ensureValidatorFound(found, address) + validator := k.mustGetValidator(ctx, address) if validator.Status == sdk.Bonded { validators[i] = validator @@ -186,554 +281,3 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { } return validators[:i] // trim } - -//_________________________________________________________________________ -// Accumulated updates to the active/bonded validator set for tendermint - -// get the most recently updated validators -// -// 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) GetTendermintUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) { - tstore := ctx.TransientStore(k.storeTKey) - - iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey) - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - var abciVal abci.ValidatorUpdate - - abciValBytes := iterator.Value() - k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal) - - pub, err := tmtypes.PB2TM.PubKey(abciVal.GetPubKey()) - if err != nil { - panic(err) - } - val, found := k.GetValidator(ctx, sdk.ValAddress(pub.Address())) - 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) - } - } - return -} - -//___________________________________________________________________________ - -// Perform all the necessary steps for when a validator changes its power. This -// function updates all validator stores as well as tendermint update store. -// It may kick out validators if a new validator is entering the bonded validator -// group. -// -// TODO: Remove above nolint, function needs to be simplified! -func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator { - 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) - 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 { - - // if the validator is already bonded and the power is increasing, we need - // perform the following: - // a) update Tendermint - // b) check if the cliff validator needs to be updated - case powerIncreasing && !validator.Jailed && - (oldFound && oldValidator.Status == sdk.Bonded): - - bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) - tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) - - if cliffValExists { - cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx)) - if bytes.Equal(cliffAddr, validator.OperatorAddr) { - k.updateCliffValidator(ctx, validator) - } - } - - // if is a new validator and the new power is less than the cliff validator - case cliffValExists && !oldFound && valPowerLTcliffPower: - // skip to completion - - // if was unbonded and the new power is less than the cliff validator - case cliffValExists && - (oldFound && oldValidator.Status == sdk.Unbonded) && - valPowerLTcliffPower: //(valPower < cliffPower - // skip to completion - - default: - // default case - validator was either: - // a) not-bonded and now has power-rank greater than cliff validator - // b) bonded and now has decreased in power - - // update the validator set for this validator - updatedVal, updated := k.UpdateBondedValidators(ctx, validator) - if updated { - // the validator has changed bonding status - validator = updatedVal - break - } - - // if decreased in power but still bonded, update Tendermint validator - if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) { - bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) - tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz) - } - } - - k.SetValidator(ctx, validator) - return validator -} - -// updateCliffValidator determines if the current cliff validator needs to be -// updated or swapped. If the provided affected validator is the current cliff -// validator before it's power was increased, either the cliff power key will -// be updated or if it's power is greater than the next bonded validator by -// power, it'll be swapped. -func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validator) { - var newCliffVal types.Validator - - store := ctx.KVStore(k.storeKey) - pool := k.GetPool(ctx) - cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx)) - - oldCliffVal, found := k.GetValidator(ctx, cliffAddr) - if !found { - panic(fmt.Sprintf("cliff validator record not found for address: %X\n", cliffAddr)) - } - - // Create a validator iterator ranging from smallest to largest by power - // starting the current cliff validator's power. - start := GetValidatorsByPowerIndexKey(oldCliffVal, pool) - end := sdk.PrefixEndBytes(ValidatorsByPowerIndexKey) - iterator := store.Iterator(start, end) - - if iterator.Valid() { - ownerAddr := iterator.Value() - currVal, found := k.GetValidator(ctx, ownerAddr) - ensureValidatorFound(found, ownerAddr) - - if currVal.Status != sdk.Bonded || currVal.Jailed { - panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr)) - } - - newCliffVal = currVal - iterator.Close() - } else { - panic("failed to create valid validator power iterator") - } - - affectedValRank := GetValidatorsByPowerIndexKey(affectedVal, pool) - 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) - } else { - panic("invariant broken: the cliff validator should change or it should remain the same") - } -} - -func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { - if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded { - newValidator = k.beginUnbondingValidator(ctx, newValidator) - - // need to also clear the cliff validator spot because the jail has - // opened up a new spot which will be filled when - // updateValidatorsBonded is called - k.clearCliffValidator(ctx) - } - return newValidator -} - -// nolint: unparam -func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool { - if oldFound && oldValidator.BondedTokens().LT(newValidator.BondedTokens()) { - return true - } - return false -} - -// get the bond height and incremented intra-tx counter -// nolint: unparam -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 - 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 -} - -func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidator, - newValidator types.Validator, pool types.Pool) (valPower []byte) { - store := ctx.KVStore(k.storeKey) - - // update the list ordered by voting power - if oldFound { - store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool)) - } - valPower = GetValidatorsByPowerIndexKey(newValidator, pool) - store.Set(valPower, newValidator.OperatorAddr) - - return valPower -} - -// Update the bonded validator group based on a change to the validator -// affectedValidator. This function potentially adds the affectedValidator to -// the bonded validator group which kicks out the cliff validator. Under this -// situation this function returns the updated affectedValidator. -// -// The correct bonded subset of validators is retrieved by iterating through an -// index of the validators sorted by power, stored using the -// ValidatorsByPowerIndexKey. Simultaneously the current validator records are -// updated in store with the ValidatorsBondedIndexKey. This store is used to -// determine if a validator is a validator without needing to iterate over all -// validators. -func (k Keeper) UpdateBondedValidators( - ctx sdk.Context, affectedValidator types.Validator) ( - updatedVal types.Validator, updated bool) { - - store := ctx.KVStore(k.storeKey) - - oldCliffValidatorAddr := k.GetCliffValidator(ctx) - maxValidators := k.GetParams(ctx).MaxValidators - bondedValidatorsCount := 0 - var validator, validatorToBond types.Validator - newValidatorBonded := false - - // create a validator iterator ranging from largest to smallest by power - iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) - for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() { - - // either retrieve the original validator from the store, or under the - // situation that this is the "affected validator" just use the - // validator provided because it has not yet been updated in the store - ownerAddr := iterator.Value() - if bytes.Equal(ownerAddr, affectedValidator.OperatorAddr) { - validator = affectedValidator - } else { - var found bool - validator, found = k.GetValidator(ctx, ownerAddr) - ensureValidatorFound(found, ownerAddr) - } - - // if we've reached jailed validators no further bonded validators exist - if validator.Jailed { - if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("jailed validator cannot be bonded, address: %X\n", ownerAddr)) - } - - 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 - } - - bondedValidatorsCount++ - } - - iterator.Close() - - if newValidatorBonded && bytes.Equal(oldCliffValidatorAddr, validator.OperatorAddr) { - panic("cliff validator has not been changed, yet we bonded a new validator") - } - - // clear or set the cliff validator - if bondedValidatorsCount == int(maxValidators) { - k.setCliffValidator(ctx, validator, k.GetPool(ctx)) - } else if len(oldCliffValidatorAddr) > 0 { - k.clearCliffValidator(ctx) - } - - // swap the cliff validator for a new validator if the affected validator - // was bonded - if newValidatorBonded { - if oldCliffValidatorAddr != nil { - oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr) - ensureValidatorFound(found, oldCliffValidatorAddr) - - if bytes.Equal(validatorToBond.OperatorAddr, affectedValidator.OperatorAddr) { - - // begin unbonding the old cliff validator iff the affected - // 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) - } - } - - validator = k.bondValidator(ctx, validatorToBond) - if bytes.Equal(validator.OperatorAddr, affectedValidator.OperatorAddr) { - return validator, true - } - - return affectedValidator, true - } - - return types.Validator{}, false -} - -// full update of the bonded validator set, many can be added/kicked -func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) - - // clear the current validators store, add to the ToKickOut temp store - toKickOut := make(map[string]byte) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) - for ; iterator.Valid(); iterator.Next() { - ownerAddr := GetAddressFromValBondedIndexKey(iterator.Key()) - toKickOut[string(ownerAddr)] = 0 - } - - iterator.Close() - - var validator types.Validator - - oldCliffValidatorAddr := k.GetCliffValidator(ctx) - maxValidators := k.GetParams(ctx).MaxValidators - bondedValidatorsCount := 0 - - iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) - for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() { - var found bool - - ownerAddr := iterator.Value() - validator, found = k.GetValidator(ctx, ownerAddr) - ensureValidatorFound(found, ownerAddr) - - _, found = toKickOut[string(ownerAddr)] - if found { - delete(toKickOut, string(ownerAddr)) - } else { - // If the validator wasn't in the toKickOut group it means it wasn't - // previously a validator, therefor update the validator to enter - // the validator group. - validator = k.bondValidator(ctx, validator) - } - - if validator.Jailed { - // we should no longer consider jailed validators as they are ranked - // lower than any non-jailed/bonded validators - if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr)) - } - break - } - - bondedValidatorsCount++ - } - - iterator.Close() - - // clear or set the cliff validator - if bondedValidatorsCount == int(maxValidators) { - k.setCliffValidator(ctx, validator, k.GetPool(ctx)) - } else if len(oldCliffValidatorAddr) > 0 { - k.clearCliffValidator(ctx) - } - - kickOutValidators(k, ctx, toKickOut) - return -} - -func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) { - for key := range toKickOut { - ownerAddr := []byte(key) - validator, found := k.GetValidator(ctx, ownerAddr) - ensureValidatorFound(found, ownerAddr) - k.beginUnbondingValidator(ctx, validator) - } -} - -// perform all the store operations for when a validator status becomes unbonded -func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { - - store := ctx.KVStore(k.storeKey) - pool := k.GetPool(ctx) - params := k.GetParams(ctx) - - // sanity check - if validator.Status == sdk.Unbonded || - validator.Status == sdk.Unbonding { - panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator)) - } - - // set the status - validator, pool = validator.UpdateStatus(pool, sdk.Unbonding) - k.SetPool(ctx, pool) - - validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) - validator.UnbondingHeight = ctx.BlockHeader().Height - - // save the now unbonded validator record - k.SetValidator(ctx, validator) - - // add to accumulated changes for tendermint - bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero()) - tstore := ctx.TransientStore(k.storeTKey) - tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) - - // also remove from the Bonded types.Validators Store - store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) - - k.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) - return validator -} - -// perform all the store operations for when a validator status becomes bonded -func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator { - - store := ctx.KVStore(k.storeKey) - pool := k.GetPool(ctx) - - // sanity check - if validator.Status == sdk.Bonded { - 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) - - // save the now bonded validator record to the three referenced stores - k.SetValidator(ctx, validator) - store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{}) - - // add to accumulated changes for tendermint - bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate()) - tstore := ctx.TransientStore(k.storeTKey) - tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) - - k.OnValidatorBonded(ctx, validator.ConsAddress()) - return validator -} - -// remove the validator record and associated indexes -func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { - - k.OnValidatorRemoved(ctx, address) - - // first retrieve the old validator record - validator, found := k.GetValidator(ctx, address) - if !found { - return - } - - // delete the old validator record - store := ctx.KVStore(k.storeKey) - pool := k.GetPool(ctx) - store.Delete(GetValidatorKey(address)) - store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) - store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) - - // delete from the current and power weighted validator groups if the validator - // is bonded - and add validator with zero power to the validator updates - if store.Get(GetValidatorsBondedIndexKey(validator.OperatorAddr)) == nil { - return - } - store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) - - bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero()) - tstore := ctx.TransientStore(k.storeTKey) - tstore.Set(GetTendermintUpdatesTKey(address), bz) -} - -// UpdateValidatorCommission attempts to update a validator's commission rate. -// An error is returned if the new commission rate is invalid. -func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) sdk.Error { - commission := validator.Commission - blockTime := ctx.BlockHeader().Time - - if err := commission.ValidateNewRate(newRate, blockTime); err != nil { - return err - } - - validator.Commission.Rate = newRate - validator.Commission.UpdateTime = blockTime - - k.SetValidator(ctx, validator) - k.OnValidatorCommissionChange(ctx, validator.OperatorAddr) - return nil -} - -//__________________________________________________________________________ - -// get the current validator on the cliff -func (k Keeper) GetCliffValidator(ctx sdk.Context) []byte { - store := ctx.KVStore(k.storeKey) - return store.Get(ValidatorCliffIndexKey) -} - -// get the current power of the validator on the cliff -func (k Keeper) GetCliffValidatorPower(ctx sdk.Context) []byte { - store := ctx.KVStore(k.storeKey) - return store.Get(ValidatorPowerCliffKey) -} - -// set the current validator and power of the validator on the cliff -func (k Keeper) setCliffValidator(ctx sdk.Context, validator types.Validator, pool types.Pool) { - store := ctx.KVStore(k.storeKey) - bz := GetValidatorsByPowerIndexKey(validator, pool) - store.Set(ValidatorPowerCliffKey, bz) - store.Set(ValidatorCliffIndexKey, validator.OperatorAddr) -} - -// clear the current validator and power of the validator on the cliff -func (k Keeper) clearCliffValidator(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) - 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)) - } -} diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 7533fed6e..9b4dc3c55 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -13,18 +13,6 @@ import ( "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) { @@ -41,11 +29,17 @@ func TestSetValidator(t *testing.T) { assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) keeper.SetPool(ctx, pool) - keeper.UpdateValidator(ctx, validator) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator, pool) - // after the save the validator should be bonded + // ensure update + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found := keeper.GetValidator(ctx, valAddr) require.True(t, found) + require.Equal(t, 1, len(updates)) + require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) + + // after the save the validator should be bonded 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)) @@ -59,7 +53,7 @@ func TestSetValidator(t *testing.T) { require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validator, resVals[0])) - resVals = keeper.GetValidatorsByPower(ctx) + resVals = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) @@ -71,10 +65,6 @@ func TestSetValidator(t *testing.T) { require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) - updates := keeper.GetTendermintUpdates(ctx) - require.Equal(t, 1, len(updates)) - require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) - allVals := keeper.GetAllValidators(ctx) require.Equal(t, 1, len(allVals)) } @@ -94,27 +84,28 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { require.Equal(t, sdk.Unbonded, validator.Status) require.Equal(t, int64(100), validator.Tokens.RoundInt64()) keeper.SetPool(ctx, pool) - keeper.UpdateValidator(ctx, validator) + testingUpdateValidator(keeper, ctx, validator) validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool) pool = keeper.GetPool(ctx) power := GetValidatorsByPowerIndexKey(validator, pool) - require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) + require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) // burn half the delegator shares + keeper.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2))) require.Equal(t, int64(50), burned.RoundInt64()) - keeper.SetPool(ctx, pool) // update the pool - keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out - require.False(t, keeper.validatorByPowerIndexExists(ctx, power)) + keeper.SetPool(ctx, pool) // update the pool + testingUpdateValidator(keeper, ctx, validator) // update the validator, possibly kicking it out + require.False(t, validatorByPowerIndexExists(keeper, ctx, power)) pool = keeper.GetPool(ctx) validator, found = keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) power = GetValidatorsByPowerIndexKey(validator, pool) - require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) + require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) } func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { @@ -144,7 +135,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10))) keeper.SetPool(ctx, pool) - val = keeper.UpdateValidator(ctx, val) + val = testingUpdateValidator(keeper, ctx, val) validators[i] = val } @@ -152,17 +143,10 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { // remove enough tokens to kick out the validator below the current cliff // validator and next in line cliff validator + keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal, pool) nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21)) keeper.SetPool(ctx, pool) - nextCliffVal = keeper.UpdateValidator(ctx, nextCliffVal) - - // require the cliff validator has changed - cliffVal := validators[numVals-maxVals-1] - require.Equal(t, cliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx))) - - // require the cliff validator power has changed - cliffPower := keeper.GetCliffValidatorPower(ctx) - require.Equal(t, GetValidatorsByPowerIndexKey(cliffVal, pool), cliffPower) + nextCliffVal = testingUpdateValidator(keeper, ctx, nextCliffVal) expectedValStatus := map[int]sdk.BondStatus{ 9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded, @@ -182,75 +166,6 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { } } -func TestCliffValidatorChange(t *testing.T) { - numVals := 10 - maxVals := 5 - - // create context, keeper, and pool for tests - ctx, _, keeper := CreateTestInput(t, false, 0) - pool := keeper.GetPool(ctx) - - // create keeper parameters - params := keeper.GetParams(ctx) - params.MaxValidators = uint16(maxVals) - keeper.SetParams(ctx, params) - - // create a random pool - pool.LooseTokens = sdk.NewDec(10000) - pool.BondedTokens = sdk.NewDec(1234) - keeper.SetPool(ctx, pool) - - validators := make([]types.Validator, numVals) - for i := 0; i < len(validators); i++ { - moniker := fmt.Sprintf("val#%d", int64(i)) - val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) - val.BondHeight = int64(i) - val.BondIntraTxCounter = int16(i) - val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10))) - - keeper.SetPool(ctx, pool) - val = keeper.UpdateValidator(ctx, val) - validators[i] = val - } - - // add a large amount of tokens to current cliff validator - currCliffVal := validators[numVals-maxVals] - currCliffVal, pool, _ = currCliffVal.AddTokensFromDel(pool, sdk.NewInt(200)) - keeper.SetPool(ctx, pool) - currCliffVal = keeper.UpdateValidator(ctx, currCliffVal) - - // assert new cliff validator to be set to the second lowest bonded validator by power - newCliffVal := validators[numVals-maxVals+1] - require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx))) - - // assert cliff validator power should have been updated - cliffPower := keeper.GetCliffValidatorPower(ctx) - require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower) - - // add small amount of tokens to new current cliff validator - newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(1)) - keeper.SetPool(ctx, pool) - newCliffVal = keeper.UpdateValidator(ctx, newCliffVal) - - // assert cliff validator has not change but increased in power - cliffPower = keeper.GetCliffValidatorPower(ctx) - require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx))) - require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower) - - // add enough power to cliff validator to be equal in rank to next validator - newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(9)) - keeper.SetPool(ctx, pool) - newCliffVal = keeper.UpdateValidator(ctx, newCliffVal) - - // assert new cliff validator due to power rank construction - newCliffVal = validators[numVals-maxVals+2] - require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx))) - - // assert cliff validator power should have been updated - cliffPower = keeper.GetCliffValidatorPower(ctx) - require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower) -} - func TestSlashToZeroPowerRemoved(t *testing.T) { // initialize setup ctx, _, keeper := CreateTestInput(t, false, 100) @@ -263,12 +178,14 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { require.Equal(t, int64(100), validator.Tokens.RoundInt64()) keeper.SetPool(ctx, pool) keeper.SetValidatorByConsAddr(ctx, validator) - validator = keeper.UpdateValidator(ctx, validator) + validator = testingUpdateValidator(keeper, ctx, validator) require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool) // slash the validator by 100% consAddr0 := sdk.ConsAddress(PKs[0].Address()) keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) // validator should have been deleted _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) @@ -306,7 +223,7 @@ func TestValidatorBasics(t *testing.T) { assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens)) // set and retrieve a record - validators[0] = keeper.UpdateValidator(ctx, validators[0]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) keeper.SetValidatorByConsAddr(ctx, validators[0]) resVal, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) @@ -333,7 +250,7 @@ func TestValidatorBasics(t *testing.T) { validators[0].Status = sdk.Bonded validators[0].Tokens = sdk.NewDec(10) validators[0].DelegatorShares = sdk.NewDec(10) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) resVal, found = keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) @@ -343,8 +260,8 @@ func TestValidatorBasics(t *testing.T) { assert.True(ValEq(t, validators[0], resVals[0])) // add other validators - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - validators[2] = keeper.UpdateValidator(ctx, validators[2]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + validators[2] = testingUpdateValidator(keeper, ctx, validators[2]) resVal, found = keeper.GetValidator(ctx, addrVals[1]) require.True(t, found) assert.True(ValEq(t, validators[1], resVal)) @@ -364,7 +281,7 @@ func TestValidatorBasics(t *testing.T) { require.False(t, found) } -// test how the validators are sorted, tests GetValidatorsByPower +// test how the validators are sorted, tests GetBondedValidatorsByPower func GetValidatorSortingUnmixed(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) @@ -377,11 +294,11 @@ func GetValidatorSortingUnmixed(t *testing.T) { validators[i].Status = sdk.Bonded validators[i].Tokens = sdk.NewDec(amt) validators[i].DelegatorShares = sdk.NewDec(amt) - keeper.UpdateValidator(ctx, validators[i]) + testingUpdateValidator(keeper, ctx, validators[i]) } // first make sure everything made it in to the gotValidator group - resValidators := keeper.GetValidatorsByPower(ctx) + resValidators := keeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) @@ -396,15 +313,15 @@ func GetValidatorSortingUnmixed(t *testing.T) { // test a basic increase in voting power validators[3].Tokens = sdk.NewDec(500) - keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) // test a decrease in voting power validators[3].Tokens = sdk.NewDec(300) - keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -412,8 +329,8 @@ func GetValidatorSortingUnmixed(t *testing.T) { // test equal voting power, different age validators[3].Tokens = sdk.NewDec(200) ctx = ctx.WithBlockHeight(10) - keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -422,8 +339,8 @@ func GetValidatorSortingUnmixed(t *testing.T) { // no change in voting power - no change in sort ctx = ctx.WithBlockHeight(20) - keeper.UpdateValidator(ctx, validators[4]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[4]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -431,12 +348,12 @@ func GetValidatorSortingUnmixed(t *testing.T) { // change in voting power of both validators, both still in v-set, no age change validators[3].Tokens = sdk.NewDec(300) validators[4].Tokens = sdk.NewDec(300) - keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) ctx = ctx.WithBlockHeight(30) - keeper.UpdateValidator(ctx, validators[4]) - resValidators = keeper.GetValidatorsByPower(ctx) + testingUpdateValidator(keeper, ctx, validators[4]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n, "%v", resValidators) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -473,7 +390,7 @@ func GetValidatorSortingMixed(t *testing.T) { validators[4].Tokens = sdk.NewDec(amts[4]) for i := range amts { - keeper.UpdateValidator(ctx, validators[i]) + testingUpdateValidator(keeper, ctx, validators[i]) } val0, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[0])) require.True(t, found) @@ -492,7 +409,7 @@ func GetValidatorSortingMixed(t *testing.T) { require.Equal(t, sdk.Bonded, val4.Status) // first make sure everything made it in to the gotValidator group - resValidators := keeper.GetValidatorsByPower(ctx) + resValidators := keeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) @@ -525,24 +442,26 @@ func TestGetValidatorsEdgeCases(t *testing.T) { moniker := fmt.Sprintf("val#%d", int64(i)) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + validators[i].BondIntraTxCounter = int16(i) keeper.SetPool(ctx, pool) - validators[i] = keeper.UpdateValidator(ctx, validators[i]) + validators[i] = testingUpdateValidator(keeper, ctx, validators[i]) } for i := range amts { validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr) require.True(t, found) } - resValidators := keeper.GetValidatorsByPower(ctx) + resValidators := keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, nMax, uint16(len(resValidators))) assert.True(ValEq(t, validators[2], resValidators[0])) assert.True(ValEq(t, validators[3], resValidators[1])) pool := keeper.GetPool(ctx) + keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool) validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(500)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, nMax, uint16(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) @@ -556,28 +475,31 @@ func TestGetValidatorsEdgeCases(t *testing.T) { validators[3], found = keeper.GetValidator(ctx, validators[3].OperatorAddr) require.True(t, found) + keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool) validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(1)) keeper.SetPool(ctx, pool) - validators[3] = keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[3] = testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, nMax, uint16(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[3], resValidators[1])) // validator 3 kicked out temporarily + keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool) validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201)) keeper.SetPool(ctx, pool) - validators[3] = keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[3] = testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, nMax, uint16(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) // validator 4 does not get spot back + keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool) validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(200)) keeper.SetPool(ctx, pool) - validators[3] = keeper.UpdateValidator(ctx, validators[3]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[3] = testingUpdateValidator(keeper, ctx, validators[3]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, nMax, uint16(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) @@ -600,34 +522,39 @@ func TestValidatorBondHeight(t *testing.T) { validators[0] = types.NewValidator(sdk.ValAddress(Addrs[0]), PKs[0], types.Description{}) validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}) validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{}) + validators[0].BondIntraTxCounter = 0 + validators[1].BondIntraTxCounter = 1 + validators[2].BondIntraTxCounter = 2 validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(200)) validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(100)) validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(100)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) //////////////////////////////////////// // If two validators both increase to the same voting power in the same block, // the one with the first transaction should become bonded - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - validators[2] = keeper.UpdateValidator(ctx, validators[2]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + validators[2] = testingUpdateValidator(keeper, ctx, validators[2]) pool = keeper.GetPool(ctx) - resValidators := keeper.GetValidatorsByPower(ctx) + resValidators := keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, uint16(len(resValidators)), params.MaxValidators) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[1], resValidators[1])) + keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool) + keeper.DeleteValidatorByPowerIndex(ctx, validators[2], pool) validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(50)) validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(50)) keeper.SetPool(ctx, pool) - validators[2] = keeper.UpdateValidator(ctx, validators[2]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[2] = testingUpdateValidator(keeper, ctx, validators[2]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) require.Equal(t, params.MaxValidators, uint16(len(resValidators))) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) } @@ -646,20 +573,21 @@ func TestFullValidatorSetPowerChange(t *testing.T) { 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)) + validators[i].BondIntraTxCounter = int16(i) keeper.SetPool(ctx, pool) - keeper.UpdateValidator(ctx, validators[i]) + testingUpdateValidator(keeper, ctx, validators[i]) } for i := range amts { var found bool validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr) require.True(t, found) } - assert.Equal(t, sdk.Unbonding, validators[0].Status) + assert.Equal(t, sdk.Unbonded, validators[0].Status) assert.Equal(t, sdk.Unbonding, validators[1].Status) assert.Equal(t, sdk.Bonded, validators[2].Status) assert.Equal(t, sdk.Bonded, validators[3].Status) assert.Equal(t, sdk.Unbonded, validators[4].Status) - resValidators := keeper.GetValidatorsByPower(ctx) + resValidators := keeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, max, len(resValidators)) assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs assert.True(ValEq(t, validators[3], resValidators[1])) @@ -668,14 +596,14 @@ func TestFullValidatorSetPowerChange(t *testing.T) { pool := keeper.GetPool(ctx) validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(600)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - resValidators = keeper.GetValidatorsByPower(ctx) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + resValidators = keeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, max, len(resValidators)) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) } -func TestGetTendermintUpdatesAllNone(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -693,17 +621,22 @@ func TestGetTendermintUpdatesAllNone(t *testing.T) { // test from nothing to something // tendermintUpdate set: {} -> {c1, c3} - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + pool := keeper.GetPool(ctx) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0], pool) + keeper.SetValidator(ctx, validators[1]) + keeper.SetValidatorByPowerIndex(ctx, validators[1], pool) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) assert.Equal(t, 2, len(updates)) - assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) - assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr) + assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) + assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) } -func TestGetTendermintUpdatesIdentical(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -714,19 +647,18 @@ func TestGetTendermintUpdatesIdentical(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(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))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) } -func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -737,24 +669,23 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // test single value change // tendermintUpdate set: {} -> {c1'} validators[0].Status = sdk.Bonded validators[0].Tokens = sdk.NewDec(600) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) } -func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20} @@ -765,10 +696,9 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} @@ -776,16 +706,16 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) { validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(190)) validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(80)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(updates)) - require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) - require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) } -func TestGetTendermintUpdatesInserted(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{10, 20, 5, 15, 25} @@ -796,36 +726,42 @@ func TestGetTendermintUpdatesInserted(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} - validators[2] = keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetTendermintUpdates(ctx) + pool := keeper.GetPool(ctx) + keeper.SetValidator(ctx, validators[2]) + keeper.SetValidatorByPowerIndex(ctx, validators[2], pool) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr) require.Equal(t, 1, len(updates)) require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} - clearTendermintUpdates(ctx, keeper) - validators[3] = keeper.UpdateValidator(ctx, validators[3]) - updates = keeper.GetTendermintUpdates(ctx) + pool = keeper.GetPool(ctx) + keeper.SetValidator(ctx, validators[3]) + keeper.SetValidatorByPowerIndex(ctx, validators[3], pool) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[3], _ = keeper.GetValidator(ctx, validators[3].OperatorAddr) require.Equal(t, 1, len(updates)) require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0]) // test validtor added at the end // tendermintUpdate set: {} -> {c0} - clearTendermintUpdates(ctx, keeper) - validators[4] = keeper.UpdateValidator(ctx, validators[4]) - updates = keeper.GetTendermintUpdates(ctx) + pool = keeper.GetPool(ctx) + keeper.SetValidator(ctx, validators[4]) + keeper.SetValidatorByPowerIndex(ctx, validators[4], pool) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[4], _ = keeper.GetValidator(ctx, validators[4].OperatorAddr) require.Equal(t, 1, len(updates)) require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0]) } -func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := types.DefaultParams() params.MaxValidators = 2 @@ -839,34 +775,33 @@ func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // test validator added at the end but not inserted in the valset // tendermintUpdate set: {} -> {} - keeper.UpdateValidator(ctx, validators[2]) - updates := keeper.GetTendermintUpdates(ctx) + testingUpdateValidator(keeper, ctx, validators[2]) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 0, len(updates)) // test validator change its power and become a gotValidator (pushing out an existing) // tendermintUpdate set: {} -> {c0, c4} - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(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) + keeper.SetValidator(ctx, validators[2]) + keeper.SetValidatorByPowerIndex(ctx, validators[2], pool) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr) require.Equal(t, 2, len(updates), "%v", updates) - require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[0]) - require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[1]) + require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1]) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) } -func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) amts := []int64{100, 100} @@ -875,12 +810,12 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { 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)) + validators[i].BondIntraTxCounter = int16(i) keeper.SetPool(ctx, pool) } - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // check initial power require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64()) @@ -892,21 +827,21 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20)) validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) + validators[0] = testingUpdateValidator(keeper, ctx, validators[0]) + validators[1] = testingUpdateValidator(keeper, ctx, validators[1]) // power has changed require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64()) require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64()) // Tendermint updates should reflect power change - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(updates)) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } -func TestGetTendermintUpdatesNewValidator(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := keeper.GetParams(ctx) params.MaxValidators = uint16(3) @@ -923,28 +858,33 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) { valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + validators[i].BondIntraTxCounter = int16(i) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) - validators[i] = keeper.UpdateValidator(ctx, validators[i]) + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i], pool) } // verify initial Tendermint updates are correct - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, len(validators), len(updates)) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // update initial validator set for i, amt := range amts { pool := keeper.GetPool(ctx) + keeper.DeleteValidatorByPowerIndex(ctx, validators[i], pool) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) - validators[i] = keeper.UpdateValidator(ctx, validators[i]) + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i], pool) } // add a new validator that goes from zero power, to non-zero power, back to @@ -958,10 +898,11 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) { validator, pool, _ = validator.AddTokensFromDel(pool, amt) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) + keeper.SetValidator(ctx, validator) validator, pool, _ = validator.RemoveDelShares(pool, sdk.NewDecFromInt(amt)) - validator = keeper.UpdateValidator(ctx, validator) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator, pool) // add a new validator that increases in power valPubKey = PKs[len(validators)+2] @@ -969,19 +910,22 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) { validator = types.NewValidator(valAddr, valPubKey, types.Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500)) - + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator, pool) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) // verify initial Tendermint updates are correct - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, _ = keeper.GetValidator(ctx, validator.OperatorAddr) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr) require.Equal(t, len(validators)+1, len(updates)) require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2]) } -func TestGetTendermintUpdatesBondTransition(t *testing.T) { +func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) params := keeper.GetParams(ctx) params.MaxValidators = uint16(2) @@ -1000,60 +944,67 @@ func TestGetTendermintUpdatesBondTransition(t *testing.T) { validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker}) validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) - + validators[i].BondIntraTxCounter = int16(i) keeper.SetPool(ctx, pool) - validators[i] = keeper.UpdateValidator(ctx, validators[i]) + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i], pool) } // verify initial Tendermint updates are correct - updates := keeper.GetTendermintUpdates(ctx) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(updates)) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr) require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(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) + var found bool + validators[0], found = keeper.GetValidator(ctx, validators[0].OperatorAddr) require.True(t, found) - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(1)) - + keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(1)) keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validator) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0], pool) // verify initial Tendermint updates are correct - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(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) + validators[1], found = keeper.GetValidator(ctx, validators[1].OperatorAddr) require.True(t, found) - validator, pool, _ = validator.RemoveDelShares(pool, validator.DelegatorShares) - + keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool) + validators[0], pool, _ = validators[0].RemoveDelShares(pool, validators[0].DelegatorShares) keeper.SetPool(ctx, pool) - validator = keeper.UpdateValidator(ctx, validator) - - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(250)) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0], pool) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 0, len(updates)) + keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool) + validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(250)) keeper.SetPool(ctx, pool) - validators[1] = keeper.UpdateValidator(ctx, validator) + keeper.SetValidator(ctx, validators[1]) + keeper.SetValidatorByPowerIndex(ctx, validators[1], pool) // verify initial Tendermint updates are correct - updates = keeper.GetTendermintUpdates(ctx) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) - clearTendermintUpdates(ctx, keeper) - require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) } func TestUpdateValidatorCommission(t *testing.T) { @@ -1072,6 +1023,9 @@ func TestUpdateValidatorCommission(t *testing.T) { val1, _ = val1.SetInitialCommission(commission1) val2, _ = val2.SetInitialCommission(commission2) + keeper.SetValidator(ctx, val1) + keeper.SetValidator(ctx, val2) + testCases := []struct { validator types.Validator newRate sdk.Dec @@ -1085,11 +1039,13 @@ func TestUpdateValidatorCommission(t *testing.T) { } for i, tc := range testCases { - err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) + commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) if tc.expectedErr { require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate) } else { + tc.validator.Commission = commission + keeper.SetValidator(ctx, tc.validator) val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddr) require.True(t, found, diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/queryable_test.go index ee52773c6..d950f90bf 100644 --- a/x/stake/querier/queryable_test.go +++ b/x/stake/querier/queryable_test.go @@ -71,8 +71,8 @@ func TestQueryValidators(t *testing.T) { validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt) } keeper.SetPool(ctx, pool) - validators[0] = keeper.UpdateValidator(ctx, validators[0]) - validators[1] = keeper.UpdateValidator(ctx, validators[1]) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidator(ctx, validators[1]) // Query Validators queriedValidators := keeper.GetValidators(ctx, params.MaxValidators) @@ -114,9 +114,14 @@ func TestQueryDelegation(t *testing.T) { // Create Validators and Delegation val1 := types.NewValidator(addrVal1, pk1, types.Description{}) keeper.SetValidator(ctx, val1) + pool := keeper.GetPool(ctx) + keeper.SetValidatorByPowerIndex(ctx, val1, pool) keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + // Query Delegator bonded validators queryParams := newTestDelegatorQuery(addrAcc2) bz, errRes := cdc.MarshalJSON(queryParams) diff --git a/x/stake/stake.go b/x/stake/stake.go index 7e60b3113..a9a3ca3cd 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -37,9 +37,7 @@ var ( GetValidatorKey = keeper.GetValidatorKey GetValidatorByConsAddrKey = keeper.GetValidatorByConsAddrKey - GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey - GetTendermintUpdatesTKey = keeper.GetTendermintUpdatesTKey GetDelegationKey = keeper.GetDelegationKey GetDelegationsKey = keeper.GetDelegationsKey ParamKey = keeper.ParamKey @@ -48,9 +46,6 @@ var ( ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey ValidatorsBondedIndexKey = keeper.ValidatorsBondedIndexKey ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey - ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey - ValidatorPowerCliffKey = keeper.ValidatorPowerCliffKey - TendermintUpdatesTKey = keeper.TendermintUpdatesTKey DelegationKey = keeper.DelegationKey IntraTxCounterKey = keeper.IntraTxCounterKey GetUBDKey = keeper.GetUBDKey diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 57794691f..dbd4e2a54 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -314,6 +314,12 @@ func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { } } +// ABCIValidatorPowerBytes +func (v Validator) ABCIValidatorPowerBytes(cdc *codec.Codec) []byte { + power := v.BondedTokens().RoundInt64() + return cdc.MustMarshalBinary(power) +} + // ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staked validator type // with zero power used for validator updates. func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { @@ -429,6 +435,7 @@ func (v Validator) BondedTokens() sdk.Dec { return sdk.ZeroDec() } +// TODO remove this once the validator queue logic is implemented // Returns if the validator should be considered unbonded func (v Validator) IsUnbonded(ctx sdk.Context) bool { switch v.Status { From 89d13d1f4c109ef9f1064ae150545f9ad75aeccc Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 3 Oct 2018 16:42:44 +0000 Subject: [PATCH 09/15] Merge PR #2429: LCD Properly Init Proposing Validator in Tests * Refactor InitializeTestLCD to initialize correctly * Update InitializeTestLCD API usage in LCD unit tests * Update pending log * Update named return variables * Increase power of validator proposer in LCD tests --- PENDING.md | 2 ++ client/lcd/lcd_test.go | 72 ++++++++++++++++++++------------------ client/lcd/test_helpers.go | 39 +++++++++++++-------- 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/PENDING.md b/PENDING.md index 18da22592..b874b9bf7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -174,5 +174,7 @@ BUG FIXES * [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker` * [simulation] \#1924 Make simulation stop on SIGTERM * [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository. + * [\#2416](https://github.com/cosmos/cosmos-sdk/issues/2416) Refactored + `InitializeTestLCD` to properly include proposing validator in genesis state. * Tendermint diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 1da8edcc8..192b99a8f 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -42,7 +42,7 @@ func init() { func TestKeys(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // get seed @@ -125,7 +125,7 @@ func TestVersion(t *testing.T) { t.SkipNow() } - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() // node info @@ -148,7 +148,7 @@ func TestVersion(t *testing.T) { } func TestNodeStatus(t *testing.T) { - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() // node info @@ -170,7 +170,7 @@ func TestNodeStatus(t *testing.T) { } func TestBlock(t *testing.T) { - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() var resultBlock ctypes.ResultBlock @@ -200,7 +200,7 @@ func TestBlock(t *testing.T) { } func TestValidators(t *testing.T) { - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() var resultVals rpc.ResultValidatorsOutput @@ -235,7 +235,7 @@ func TestValidators(t *testing.T) { func TestCoinSend(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6") @@ -303,7 +303,7 @@ func TestCoinSend(t *testing.T) { func DisabledTestIBCTransfer(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() acc := getAccount(t, port, addr) @@ -332,7 +332,7 @@ func DisabledTestIBCTransfer(t *testing.T) { func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() acc := getAccount(t, port, addr) @@ -391,7 +391,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { func TestTxs(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // query wrong @@ -450,7 +450,7 @@ func TestTxs(t *testing.T) { func TestPoolParamsQuery(t *testing.T) { _, password := "test", "1234567890" addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() defaultParams := stake.DefaultParams() @@ -484,9 +484,11 @@ func TestPoolParamsQuery(t *testing.T) { } func TestValidatorsQuery(t *testing.T) { - cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() - require.Equal(t, 1, len(pks)) + + require.Equal(t, 1, len(valPubKeys)) + require.Equal(t, 1, len(operAddrs)) validators := getValidators(t, port) require.Equal(t, len(validators), 1) @@ -494,35 +496,35 @@ func TestValidatorsQuery(t *testing.T) { // make sure all the validators were found (order unknown because sorted by operator addr) foundVal := false - if validators[0].ConsPubKey == pks[0] { + if validators[0].ConsPubKey == valPubKeys[0] { foundVal = true } - require.True(t, foundVal, "pk %v, operator %v", pks[0], validators[0].OperatorAddr) + require.True(t, foundVal, "pk %v, operator %v", operAddrs[0], validators[0].OperatorAddr) } func TestValidatorQuery(t *testing.T) { - cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() - require.Equal(t, 1, len(pks)) + require.Equal(t, 1, len(valPubKeys)) + require.Equal(t, 1, len(operAddrs)) - validator1Operator := sdk.ValAddress(pks[0].Address()) - validator := getValidator(t, port, validator1Operator) - assert.Equal(t, validator.OperatorAddr, validator1Operator, "The returned validator does not hold the correct data") + validator := getValidator(t, port, operAddrs[0]) + assert.Equal(t, validator.OperatorAddr, operAddrs[0], "The returned validator does not hold the correct data") } func TestBonding(t *testing.T) { name, password, denom := "test", "1234567890", "steak" addr, seed := CreateAddr(t, name, password, GetKeyBase(t)) - cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + + cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() amt := sdk.NewDec(60) - validator1Operator := sdk.ValAddress(pks[0].Address()) - validator := getValidator(t, port, validator1Operator) + validator := getValidator(t, port, operAddrs[0]) // create bond TX - resultTx := doDelegate(t, port, seed, name, password, addr, validator1Operator, 60) + resultTx := doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -534,7 +536,7 @@ func TestBonding(t *testing.T) { require.Equal(t, int64(40), coins.AmountOf(denom).Int64()) // query validator - bond := getDelegation(t, port, addr, validator1Operator) + bond := getDelegation(t, port, addr, operAddrs[0]) require.Equal(t, amt, bond.Shares) summary := getDelegationSummary(t, port, addr) @@ -545,17 +547,17 @@ func TestBonding(t *testing.T) { bondedValidators := getDelegatorValidators(t, port, addr) require.Len(t, bondedValidators, 1) - require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr) + require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr) require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String()) - bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator) - require.Equal(t, validator1Operator, bondedValidator.OperatorAddr) + bondedValidator := getDelegatorValidator(t, port, addr, operAddrs[0]) + require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr) ////////////////////// // testing unbonding // create unbond TX - resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Operator, 60) + resultTx = doBeginUnbonding(t, port, seed, name, password, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -566,7 +568,7 @@ func TestBonding(t *testing.T) { coins = acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) - unbonding := getUndelegation(t, port, addr, validator1Operator) + unbonding := getUndelegation(t, port, addr, operAddrs[0]) require.Equal(t, "60", unbonding.Balance.Amount.String()) summary = getDelegationSummary(t, port, addr) @@ -599,7 +601,7 @@ func TestBonding(t *testing.T) { func TestSubmitProposal(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX @@ -621,7 +623,7 @@ func TestSubmitProposal(t *testing.T) { func TestDeposit(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX @@ -655,7 +657,7 @@ func TestDeposit(t *testing.T) { func TestVote(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX @@ -693,12 +695,12 @@ func TestVote(t *testing.T) { func TestUnjail(t *testing.T) { _, password := "test", "1234567890" addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // XXX: any less than this and it fails tests.WaitForHeight(3, port) - pkString, _ := sdk.Bech32ifyConsPub(pks[0]) + pkString, _ := sdk.Bech32ifyConsPub(valPubKeys[0]) signingInfo := getSigningInfo(t, port, pkString) tests.WaitForHeight(4, port) require.Equal(t, true, signingInfo.IndexOffset > 0) @@ -711,7 +713,7 @@ func TestProposalsQuery(t *testing.T) { name2, password2 := "test2", "1234567890" addr, seed := CreateAddr(t, "test", password1, GetKeyBase(t)) addr2, seed2 := CreateAddr(t, "test2", password2, GetKeyBase(t)) - cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2}) defer cleanup() // Addr1 proposes (and deposits) proposals #1 and #2 diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 478620d33..83c570913 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -115,7 +115,14 @@ func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.Acc // their respective sockets where nValidators is the total number of validators // and initAddrs are the accounts to initialize with some steak tokens. It // returns a cleanup function, a set of validator public keys, and a port. -func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) { +func InitializeTestLCD( + t *testing.T, nValidators int, initAddrs []sdk.AccAddress, +) (cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) { + + if nValidators < 1 { + panic("InitializeTestLCD must use at least one validator") + } + config := GetConfig() config.Consensus.TimeoutCommit = 100 config.Consensus.SkipTimeoutCommit = false @@ -136,30 +143,34 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) require.NoError(t, err) - if nValidators < 1 { - panic("InitializeTestLCD must use at least one validator") + // append initial (proposing) validator + genDoc.Validators[0] = tmtypes.GenesisValidator{ + PubKey: privVal.GetPubKey(), + Power: 999999, // create enough power to enable 2/3 voting power + Name: "validator-1", } + // append any additional (non-proposing) validators for i := 1; i < nValidators; i++ { genDoc.Validators = append(genDoc.Validators, tmtypes.GenesisValidator{ PubKey: ed25519.GenPrivKey().PubKey(), Power: 1, - Name: "val", + Name: fmt.Sprintf("validator-%d", i+1), }, ) } - var validatorsPKs []crypto.PubKey - - // NOTE: It's bad practice to reuse public key address for the operator - // address but doing in the test for simplicity. var appGenTxs []json.RawMessage - for _, gdValidator := range genDoc.Validators { - pk := gdValidator.PubKey - validatorsPKs = append(validatorsPKs, pk) - appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1") + for _, gdValidator := range genDoc.Validators { + operAddr := ed25519.GenPrivKey().PubKey().Address() + pk := gdValidator.PubKey + + valConsPubKeys = append(valConsPubKeys, pk) + valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) + + appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(operAddr), gdValidator.Name) require.NoError(t, err) appGenTxs = append(appGenTxs, appGenTx) @@ -203,14 +214,14 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress tests.WaitForLCDStart(port) tests.WaitForHeight(1, port) - cleanup := func() { + cleanup = func() { logger.Debug("cleaning up LCD initialization") node.Stop() node.Wait() lcd.Close() } - return cleanup, validatorsPKs, port + return cleanup, valConsPubKeys, valOperAddrs, port } // startTM creates and starts an in-process Tendermint node with memDB and From f8cc0af1447bf709f58ff8a74657a8684994474d Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Wed, 3 Oct 2018 19:11:34 +0200 Subject: [PATCH 10/15] Merge PR #2403: Update doc on website --- docs/.vuepress/config.js | 20 ++ docs/introduction/tendermint-cosmos.md | 2 +- .../simple-governance/app-cli.md | 23 -- .../simple-governance/app-codec.md | 21 -- .../simple-governance/app-commands.md | 9 - .../simple-governance/app-constructor.md | 61 ---- .../simple-governance/app-init.md | 2 +- .../simple-governance/app-makefile.md | 22 -- .../simple-governance/app-rest.md | 57 ---- .../simple-governance/app-structure.md | 55 --- .../simple-governance/bridging-it-all.md | 256 ++++++++++++++ .../simple-governance/cast-vote.md | 19 -- .../simple-governance/intro.md | 58 ++-- .../simple-governance/module-cli.md | 33 -- .../simple-governance/module-codec.md | 13 - .../simple-governance/module-errors.md | 7 - .../simple-governance/module-handler.md | 73 ---- .../simple-governance/module-init.md | 31 -- .../simple-governance/module-keeper.md | 96 ------ .../simple-governance/module-rest.md | 32 -- .../simple-governance/module-types.md | 23 -- .../simple-governance/run-install.md | 18 - .../running-the-application.md | 77 +++++ .../{app-design.md => setup-and-design.md} | 44 +++ .../simple-governance/setup.md | 32 -- .../simple-governance/simple-gov-module.md | 316 ++++++++++++++++++ .../simple-governance/start.md | 10 - .../simple-governance/submit-proposal.md | 36 -- 28 files changed, 743 insertions(+), 703 deletions(-) delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-cli.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-codec.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-commands.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-constructor.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-makefile.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-rest.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/app-structure.md create mode 100644 docs/sdk/sdk-by-examples/simple-governance/bridging-it-all.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/cast-vote.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-cli.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-codec.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-errors.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-handler.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-init.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-keeper.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-rest.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-types.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/run-install.md create mode 100644 docs/sdk/sdk-by-examples/simple-governance/running-the-application.md rename docs/sdk/sdk-by-examples/simple-governance/{app-design.md => setup-and-design.md} (81%) delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/setup.md create mode 100644 docs/sdk/sdk-by-examples/simple-governance/simple-gov-module.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/start.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 5a30b4423..32a71dd0c 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -15,6 +15,7 @@ module.exports = { collapsable: false, children: [ "/introduction/cosmos-hub", + "/introduction/tendermint-cosmos" "/introduction/tendermint", ] }, @@ -54,6 +55,18 @@ module.exports = { // "/specs/icts", // ] // }, + { + title: "SDK by Examples - Simple Governance", + collapsable: false, + children: [ + ["/sdk/sdk-by-examples/simple-governance/intro", "Intro"], + "/sdk/sdk-by-examples/simple-governance/setup-and-design", + "/sdk/sdk-by-examples/simple-governance/app-init", + "/sdk/sdk-by-examples/simple-governance/simple-gov-module", + "/sdk/sdk-by-examples/simple-governance/bridging-it-all", + "/sdk/sdk-by-examples/simple-governance/running-the-application" + ] + }, { title: "Lotion JS", collapsable: false, @@ -71,6 +84,13 @@ module.exports = { "/validators/validator-faq" ] }, + { + title: "Clients", + collapsable: false, + children: [ + ["/clients/service-providers", "Service Providers"] + ] + }, { title: "Resources", collapsable: false, diff --git a/docs/introduction/tendermint-cosmos.md b/docs/introduction/tendermint-cosmos.md index f48106a1d..f7aeb7746 100644 --- a/docs/introduction/tendermint-cosmos.md +++ b/docs/introduction/tendermint-cosmos.md @@ -1,4 +1,4 @@ -## Tendermint and Cosmos +# Tendermint and Cosmos Blockchains can be divided into three conceptual layers: diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-cli.md b/docs/sdk/sdk-by-examples/simple-governance/app-cli.md deleted file mode 100644 index 52590c62f..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-cli.md +++ /dev/null @@ -1,23 +0,0 @@ -## Application CLI - -**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)** - -To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands: - -```go -// cmd/simplegovcli/main.go -... - rootCmd.AddCommand( - client.GetCommands( - simplegovcmd.GetCmdQueryProposal("proposals", cdc), - simplegovcmd.GetCmdQueryProposals("proposals", cdc), - simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc), - simplegovcmd.GetCmdQueryProposalVote("proposals", cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - simplegovcmd.PostCmdPropose(cdc), - simplegovcmd.PostCmdVote(cdc), - )...) -... -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-codec.md b/docs/sdk/sdk-by-examples/simple-governance/app-codec.md deleted file mode 100644 index d994ba19c..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-codec.md +++ /dev/null @@ -1,21 +0,0 @@ -## Application codec - -**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** - -Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules. - -```go -func MakeCodec() *codec.Codec { - var cdc = codec.New() - codec.RegisterCrypto(cdc) // Register crypto. - sdk.RegisterCodec(cdc) // Register Msgs - bank.RegisterCodec(cdc) - simplestake.RegisterCodec(cdc) - simpleGov.RegisterCodec(cdc) - - // Register AppAccount - cdc.RegisterInterface((*auth.Account)(nil), nil) - cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil) - return cdc -} -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-commands.md b/docs/sdk/sdk-by-examples/simple-governance/app-commands.md deleted file mode 100644 index 4eb2f9221..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-commands.md +++ /dev/null @@ -1,9 +0,0 @@ -## App commands - -We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory: - -```bash -// At root level of directory -cd cmd -``` -`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md b/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md deleted file mode 100644 index 149e97277..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md +++ /dev/null @@ -1,61 +0,0 @@ -## Application constructor - -**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** - -Now, we need to define the constructor for our application. - -```go -func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp -``` - -In this function, we will: - -- Create the codec - -```go -var cdc = MakeCodec() -``` - -- Instantiate our application. This includes creating the keys to access each of the substores. - -```go -// Create your application object. - var app = &SimpleGovApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), - cdc: cdc, - capKeyMainStore: sdk.NewKVStoreKey("main"), - capKeyAccountStore: sdk.NewKVStoreKey("acc"), - capKeyStakingStore: sdk.NewKVStoreKey("stake"), - capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"), - } -``` - -- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it. - -```go -app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) -app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace)) -app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace)) -``` - -- Declare the handlers. - -```go -app.Router(). - AddRoute("bank", bank.NewHandler(app.bankKeeper)). - AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)). - AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper)) -``` - -- Initialize the application. - -```go -// Initialize BaseApp. - app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore) - app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - err := app.LoadLatestVersion(app.capKeyMainStore) - if err != nil { - cmn.Exit(err.Error()) - } - return app -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-init.md b/docs/sdk/sdk-by-examples/simple-governance/app-init.md index a71be2bed..2417bd88b 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/app-init.md +++ b/docs/sdk/sdk-by-examples/simple-governance/app-init.md @@ -1,4 +1,4 @@ -## Application initialization +# Application Initialization In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app. diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md b/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md deleted file mode 100644 index b7502d339..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md +++ /dev/null @@ -1,22 +0,0 @@ -## Makefile - -The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it: - -``` -// Makefile -build_examples: -ifeq ($(OS),Windows_NT) - ... - go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd - go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli -else - ... - go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd - go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli -endif -... -install_examples: - ... - go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd - go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-rest.md b/docs/sdk/sdk-by-examples/simple-governance/app-rest.md deleted file mode 100644 index 828d52c85..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-rest.md +++ /dev/null @@ -1,57 +0,0 @@ -##### Rest server - -**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)** - -The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions: - -```go -// cmd/simplegovd/main.go -// SimpleGovAppInit initial parameters -var SimpleGovAppInit = server.AppInit{ - AppGenState: SimpleGovAppGenState, - AppGenTx: server.SimpleAppGenTx, -} - -// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state -func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { - appState, err = server.SimpleAppGenState(cdc, appGenTxs) - if err != nil { - return - } - return -} - -func newApp(logger log.Logger, db dbm.DB) abci.Application { - return app.NewSimpleGovApp(logger, db) -} - -func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { - dapp := app.NewSimpleGovApp(logger, db) - return dapp.ExportAppStateJSON() -} -``` - -Now, let us define the command for the daemon server within the `main()` function: - -```go -// cmd/simplegovd/main.go -func main() { - cdc := app.MakeCodec() - ctx := server.NewDefaultContext() - - rootCmd := &cobra.Command{ - Use: "simplegovd", - Short: "Simple Governance Daemon (server)", - PersistentPreRunE: server.PersistentPreRunEFn(ctx), - } - - server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit, - server.ConstructAppCreator(newApp, "simplegov"), - server.ConstructAppExporter(exportAppState, "simplegov")) - - // prepare and add flags - rootDir := os.ExpandEnv("$HOME/.simplegovd") - executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) - executor.Execute() -} -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-structure.md b/docs/sdk/sdk-by-examples/simple-governance/app-structure.md deleted file mode 100644 index ce9ba5fef..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/app-structure.md +++ /dev/null @@ -1,55 +0,0 @@ -## Application structure - -Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory. - - -```bash -// At root level of directory -cd app -``` - -We are ready to create our simple governance application! - -*Note: You can check the full file (with comments!) [here](link)* - -The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed. - -Secondly, we need to define the name of our application. - -```go -const ( - appName = "SimpleGovApp" -) -``` - -Then, let us define the structure of our application. - -```go -// Extended ABCI application -type SimpleGovApp struct { - *bam.BaseApp - cdc *codec.Codec - - // keys to access the substores - capKeyMainStore *sdk.KVStoreKey - capKeyAccountStore *sdk.KVStoreKey - capKeyStakingStore *sdk.KVStoreKey - capKeySimpleGovStore *sdk.KVStoreKey - - // keepers - feeCollectionKeeper auth.FeeCollectionKeeper - bankKeeper bank.Keeper - stakeKeeper simplestake.Keeper - simpleGovKeeper simpleGov.Keeper - - // Manage getting and setting accounts - accountMapper auth.AccountMapper -} -``` - -- Each application builds on top of the `BaseApp` template, hence the pointer. -- `cdc` is the codec used in our application. -- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store. -- Then come the keepers and mappers. - -Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/bridging-it-all.md b/docs/sdk/sdk-by-examples/simple-governance/bridging-it-all.md new file mode 100644 index 000000000..aed6de85e --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/bridging-it-all.md @@ -0,0 +1,256 @@ +# From Module To Application + +## Application structure + +Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory. + + +```bash +// At root level of directory +cd app +``` + +We are ready to create our simple governance application! + +*Note: You can check the full file (with comments!) [here](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)* + +The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed. + +Secondly, we need to define the name of our application. + +```go +const ( + appName = "SimpleGovApp" +) +``` + +Then, let us define the structure of our application. + +```go +// Extended ABCI application +type SimpleGovApp struct { + *bam.BaseApp + cdc *codec.Codec + + // keys to access the substores + capKeyMainStore *sdk.KVStoreKey + capKeyAccountStore *sdk.KVStoreKey + capKeyStakingStore *sdk.KVStoreKey + capKeySimpleGovStore *sdk.KVStoreKey + + // keepers + feeCollectionKeeper auth.FeeCollectionKeeper + bankKeeper bank.Keeper + stakeKeeper simplestake.Keeper + simpleGovKeeper simpleGov.Keeper + + // Manage getting and setting accounts + accountMapper auth.AccountMapper +} +``` + +- Each application builds on top of the `BaseApp` template, hence the pointer. +- `cdc` is the codec used in our application. +- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store. +- Then come the keepers and mappers. + +Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use. + +## CLI and Rest server + +We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory: + +```bash +// At root level of directory +cd cmd +``` +`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application. + +### Application CLI + +**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)** + +To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands: + +```go +// cmd/simplegovcli/main.go +... + rootCmd.AddCommand( + client.GetCommands( + simplegovcmd.GetCmdQueryProposal("proposals", cdc), + simplegovcmd.GetCmdQueryProposals("proposals", cdc), + simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc), + simplegovcmd.GetCmdQueryProposalVote("proposals", cdc), + )...) + rootCmd.AddCommand( + client.PostCommands( + simplegovcmd.PostCmdPropose(cdc), + simplegovcmd.PostCmdVote(cdc), + )...) +... +``` + +### Rest server + +**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)** + +The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions: + +```go +// cmd/simplegovd/main.go +// SimpleGovAppInit initial parameters +var SimpleGovAppInit = server.AppInit{ + AppGenState: SimpleGovAppGenState, + AppGenTx: server.SimpleAppGenTx, +} + +// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state +func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { + appState, err = server.SimpleAppGenState(cdc, appGenTxs) + if err != nil { + return + } + return +} + +func newApp(logger log.Logger, db dbm.DB) abci.Application { + return app.NewSimpleGovApp(logger, db) +} + +func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { + dapp := app.NewSimpleGovApp(logger, db) + return dapp.ExportAppStateJSON() +} +``` + +Now, let us define the command for the daemon server within the `main()` function: + +```go +// cmd/simplegovd/main.go +func main() { + cdc := app.MakeCodec() + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ + Use: "simplegovd", + Short: "Simple Governance Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + + server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit, + server.ConstructAppCreator(newApp, "simplegov"), + server.ConstructAppExporter(exportAppState, "simplegov")) + + // prepare and add flags + rootDir := os.ExpandEnv("$HOME/.simplegovd") + executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) + executor.Execute() +} +``` + +## Makefile + +The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it: + +``` +// Makefile +build_examples: +ifeq ($(OS),Windows_NT) + ... + go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd + go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli +else + ... + go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd + go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli +endif +... +install_examples: + ... + go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd + go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli +``` + +## Application constructor + +**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** + +Now, we need to define the constructor for our application. + +```go +func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp +``` + +In this function, we will: + +- Create the codec + +```go +var cdc = MakeCodec() +``` + +- Instantiate our application. This includes creating the keys to access each of the substores. + +```go +// Create your application object. + var app = &SimpleGovApp{ + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + cdc: cdc, + capKeyMainStore: sdk.NewKVStoreKey("main"), + capKeyAccountStore: sdk.NewKVStoreKey("acc"), + capKeyStakingStore: sdk.NewKVStoreKey("stake"), + capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"), + } +``` + +- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it. + +```go +app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) +app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace)) +app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace)) +``` + +- Declare the handlers. + +```go +app.Router(). + AddRoute("bank", bank.NewHandler(app.bankKeeper)). + AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)). + AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper)) +``` + +- Initialize the application. + +```go +// Initialize BaseApp. + app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore) + app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) + err := app.LoadLatestVersion(app.capKeyMainStore) + if err != nil { + cmn.Exit(err.Error()) + } + return app +``` + +## Application codec + +**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** + +Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules. + +```go +func MakeCodec() *codec.Codec { + var cdc = codec.New() + codec.RegisterCrypto(cdc) // Register crypto. + sdk.RegisterCodec(cdc) // Register Msgs + bank.RegisterCodec(cdc) + simplestake.RegisterCodec(cdc) + simpleGov.RegisterCodec(cdc) + + // Register AppAccount + cdc.RegisterInterface((*auth.Account)(nil), nil) + cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil) + return cdc +} +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md b/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md deleted file mode 100644 index f16f2b4e6..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md +++ /dev/null @@ -1,19 +0,0 @@ -## Cast a vote to an existing proposal - -Let's cast a vote on the created proposal: - -```bash -simplegovcli vote --proposal-id=1 --option="No" -``` - -Get the value of the option from your casted vote : - -```bash -simplegovcli proposal-vote 1 -``` - -You can also check all the casted votes of a proposal: - -```bash -simplegovcli proposals-votes 1 -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/intro.md b/docs/sdk/sdk-by-examples/simple-governance/intro.md index fe1c7639e..9b3e417e0 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/intro.md +++ b/docs/sdk/sdk-by-examples/simple-governance/intro.md @@ -6,45 +6,43 @@ Before getting in the bulk of the code, we will start by some introductory conte ## Table of contents: -### Introduction - Prerequsite reading +### Introduction - Prerequisite reading -- [Intro to Tendermint and Cosmos](../../../introduction/tendermint-cosmos.md) -- [Tendermint Core and ABCI](../../../introduction/tendermint.md) -- [Intro to Cosmos-SDK](../../overview.md) -- [Starting your own project](start.md) +- [Intro to Tendermint and Cosmos](/introduction/tendermint-cosmos.md) +- [Tendermint Core and ABCI](/introduction/tendermint.md) +- [Intro to Cosmos-SDK](/sdk/overview.md) -### Setup and design phase -- [Setup](setup.md) -- [Application design](app-design.md) +### [Setup and design](setup-and-design.md) + +- [Starting your own project](setup-and-design.md#get-started) +- [Setup](setup-and-design.md#setup) +- [Application design](setup-and-design.md#application-design) ### Implementation of the application **Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details** - [Application initialization](app-init.md) -- Simple Governance module - + [Module initialization](module-init.md) - + [Types](module-types.md) - + [Keeper](module-keeper.md) - + [Handler](module-handler.md) - + [Wire](module-codec.md) - + [Errors](module-errors.md) - + Command-Line Interface and Rest API - * [Command-Line Interface](module-cli.md) - * [Rest API](module-rest.md) -- Bridging it all together - + [Application structure](app-structure.md) - + [Application CLI and Rest Server](app-commands.md) - * [Application CLI](app-cli.md) - * [Rest Server](app-rest.md) - + [Makefile](app-makefile.md) - + [Application constructor](app-constructor.md) - + [Application codec](app-codec.md) -- Running the application - + [Installation](run-install.md) - + [Submit a proposal](submit-proposal.md) - + [Cast a vote](cast-vote.md) +- [Simple Governance module](simple-gov-module.md) + + [Module initialization](simple-gov-module.md#module-initialization) + + [Types](simple-gov-module.md#types) + + [Keeper](simple-gov-module.md#keeper) + + [Handler](simple-gov-module.md#handler) + + [Wire](simple-gov-module.md#codec) + + [Errors](simple-gov-module.md#errors) + + [Command-Line Interface](simple-gov-module.md#command-line-interface) + + [Rest API](simple-gov-module.md#rest-api) +- [Bridging it all together](bridging-it-all.md) + + [Application structure](bridging-it-all.md#application-structure) + + [Application CLI and Rest Server](bridging-it-all.md#cli-and-rest-server) + + [Makefile](bridging-it-all.md#makefile) + + [Application constructor](bridging-it-all.md#application-constructor) + + [Application codec](bridging-it-all.md#application-codec) +- [Running the application](running-the-application.md) + + [Installation](running-the-application.md#installation) + + [Submit a proposal](running-the-application.md#submit-a-proposal) + + [Cast a vote](running-the-application.md#cast-a-vote) ## Useful links diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-cli.md b/docs/sdk/sdk-by-examples/simple-governance/module-cli.md deleted file mode 100644 index 4cdf331a9..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-cli.md +++ /dev/null @@ -1,33 +0,0 @@ -## Command-Line Interface (CLI) - -**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)** - -Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module. - -The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra: - -```go - // Declare flags - const( - Flag = "flag" - ... - ) - - // Main command function. One function for each command. - func Command(codec *codec.Codec) *cobra.Command { - // Create the command to return - command := &cobra.Command{ - Use: "actual command", - Short: "Short description", - Run: func(cmd *cobra.Command, args []string) error { - // Actual function to run when command is used - }, - } - - // Add flags to the command - command.Flags().(FlagNameConstant, , "") - - return command - } -``` - diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-codec.md b/docs/sdk/sdk-by-examples/simple-governance/module-codec.md deleted file mode 100644 index 68b03b936..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-codec.md +++ /dev/null @@ -1,13 +0,0 @@ -## Codec - -**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)** - -The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare: - -```go -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil) - cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil) -} -``` -Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more). \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-errors.md b/docs/sdk/sdk-by-examples/simple-governance/module-errors.md deleted file mode 100644 index 5597792a4..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-errors.md +++ /dev/null @@ -1,7 +0,0 @@ -## Errors - -**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)** - -The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory. - -Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-handler.md b/docs/sdk/sdk-by-examples/simple-governance/module-handler.md deleted file mode 100644 index a879c3174..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-handler.md +++ /dev/null @@ -1,73 +0,0 @@ -## Handler - -**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)** - -### Constructor and core handlers - -Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function. - -In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`: - -```go -func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case SubmitProposalMsg: - return handleSubmitProposalMsg(ctx, k, msg) - case VoteMsg: - return handleVoteMsg(ctx, k, msg) - default: - errMsg := "Unrecognized gov Msg type: " + msg.Name() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} -``` - -The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures: - -```go -func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result -``` - -Let us take a look at the parameters of this function: - -- The context `ctx` to access the stores. -- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case). -- The message `msg` that holds all the information provided by the sender of the transaction. - -The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info. - -### BeginBlocker and EndBlocker - -In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications. - -This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether. - -`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern): - -```go -func NewEndBlocker(k Keeper) sdk.EndBlocker { - return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) { - err := checkProposal(ctx, k) - if err != nil { - panic(err) - } - return - } -} -``` - -Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md). - -For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed: - -1. Get the oldest proposal from the `ProposalProcessingQueue` -2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit. -3. Check if proposal is accepted or rejected. Update the proposal status. -4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1. - -Let us perform a quick safety analysis on this process. -- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite -- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue. -- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-init.md b/docs/sdk/sdk-by-examples/simple-governance/module-init.md deleted file mode 100644 index 33bdfd500..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-init.md +++ /dev/null @@ -1,31 +0,0 @@ -## Module initialization - -First, let us go into the module's folder and create a folder for our module. - -```bash -cd x/ -mkdir simple_governance -cd simple_governance -mkdir -p client/cli client/rest -touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go -``` - -Let us start by adding the files we will need. Your module's folder should look something like that: - -``` -x -└─── simple_governance - ├─── client - │ ├─── cli - │ │ └─── simple_governance.go - │ └─── rest - │ └─── simple_governance.go - ├─── errors.go - ├─── handler.go - ├─── keeper_keys.go - ├─── keeper.go - ├─── types.go - └─── codec.go -``` - -Let us go into the detail of each of these files. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md b/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md deleted file mode 100644 index 99c7cd566..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md +++ /dev/null @@ -1,96 +0,0 @@ -## Keeper - -**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)** - -### Short intro to keepers - -`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos. - - -As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that: - -```go -// in app.go - -// instanciate keepers -keeperA = moduleA.newKeeper(app.moduleAStoreKey) -keeperB = moduleB.newKeeper(app.moduleBStoreKey) - -// pass instance of keeperA to handler of module B -app.Router(). - AddRoute("moduleA", moduleA.NewHandler(keeperA)). - AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance -``` - -`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper. - -### Store for our app - -Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them. - -- `Proposals` will be indexed by `'proposals'|`. -- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'||'votes'|`. - -Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`. - -These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, )`. - -### Keepers for our app - -In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use. - -Now, let us try to think about which keeper from **external** modules our module's keepers need access to. -Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler. - -With all that in mind, we can define the structure of our `Keeper`: - -```go - type Keeper struct { - SimpleGov sdk.StoreKey // Key to our module's store - cdc *codec.Codec // Codec to encore/decode structs - ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance - sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store. - codespace sdk.CodespaceType // Reserves space for error codes - } -``` - -And the structure of our `KeeperRead`: - -```go -type KeeperRead struct { - Keeper -} -``` - -`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store. - -### Functions and Methods - -The first function we have to create is the constructor. - -```go -func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper -``` - -This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`. - -```go -func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead -``` - -Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level. - -*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers* - -Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`. - -- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read. -- `SetProposal`: Set a `Proposal` at index `'proposals'|`. Proposals need to be encoded to `byte` before they can be stored. -- `NewProposalID`: A function to generate a new unique `proposalID`. -- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`. -- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`. -- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period. - -The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error. - -*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-rest.md b/docs/sdk/sdk-by-examples/simple-governance/module-rest.md deleted file mode 100644 index 51758f2f4..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-rest.md +++ /dev/null @@ -1,32 +0,0 @@ -## Rest API - -**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)** - -The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**. - -________________________________________________________ - -USER INTERFACE <=======> REST SERVER <=======> FULL-NODE - -________________________________________________________ - -It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`. - -- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally. - -- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally. - -Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder. - -| Method | URL | Description | -|--------|---------------------------------|-------------------------------------------------------------| -| GET | /proposals | Range query to get all submitted proposals | -| POST | /proposals | Submit a new proposal | -| GET | /proposals/{id} | Returns a proposal given its ID | -| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal | -| POST | /proposals/{id}/votes | Cast a vote on a given proposal | -| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal | - -It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it. - -Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-types.md b/docs/sdk/sdk-by-examples/simple-governance/module-types.md deleted file mode 100644 index 3b08448a6..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-types.md +++ /dev/null @@ -1,23 +0,0 @@ -## Types - -**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)** - -In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section. - -For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in. - -Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type: - -- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method. -- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message. -- `GetSigners()`: Returns address(es) of the signer(s). -- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction. -- `Get()`: A basic getter, returns some property of the message. -- `String()`: Returns a human-readable version of the message - -For our simple governance messages, this means: - -- `Type()` will return `"simpleGov"` -- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit -- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative. -- As for other methods, less customization is required. You can check the code to see a standard way of implementing these. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/run-install.md b/docs/sdk/sdk-by-examples/simple-governance/run-install.md deleted file mode 100644 index dbbf203de..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/run-install.md +++ /dev/null @@ -1,18 +0,0 @@ -## Installation - -Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application: - -```bash -go get github.com//cosmos-sdk -cd $GOPATH/src/github.com//cosmos-sdk -make get_vendor_deps -make install -make install_examples -``` - -Check that the app is correctly installed by typing: - -```bash -simplegovcli -h -simplegovd -h -``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/running-the-application.md b/docs/sdk/sdk-by-examples/simple-governance/running-the-application.md new file mode 100644 index 000000000..3a057054d --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/running-the-application.md @@ -0,0 +1,77 @@ +# Running The Application + +## Installation + +Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application: + +```bash +go get github.com//cosmos-sdk +cd $GOPATH/src/github.com//cosmos-sdk +make get_vendor_deps +make install +make install_examples +``` + +Check that the app is correctly installed by typing: + +```bash +simplegovcli -h +simplegovd -h +``` + +## Submit a proposal + +Uuse the CLI to create a new proposal: + +```bash +simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms +``` + +Or, via a json file: + +```bash +simplegovcli propose --proposal="path/to/proposal.json" +``` + +Where proposal.json contains: + +```json +{ + "title": "Voting Period Update", + "description": "Should we change the proposal voting period to 3 weeks?", + "type": "Text", + "deposit": "300Atoms" +} +``` + +Get the details of your newly created proposal: + +```bash +simplegovcli proposal 1 +``` + +You can also check all the existing open proposals: + +```bash +simplegovcli proposals --active=true +``` + +## Cast a vote + +Let's cast a vote on the created proposal: + +```bash +simplegovcli vote --proposal-id=1 --option="No" +``` + +Get the value of the option from your casted vote : + +```bash +simplegovcli proposal-vote 1 +``` + +You can also check all the casted votes of a proposal: + +```bash +simplegovcli proposals-votes 1 +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-design.md b/docs/sdk/sdk-by-examples/simple-governance/setup-and-design.md similarity index 81% rename from docs/sdk/sdk-by-examples/simple-governance/app-design.md rename to docs/sdk/sdk-by-examples/simple-governance/setup-and-design.md index fcc050979..e979b3954 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/app-design.md +++ b/docs/sdk/sdk-by-examples/simple-governance/setup-and-design.md @@ -1,3 +1,47 @@ +# Setup And Design + +## Get started + +To get started, you just have to follow these simple steps: + +1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo +2. Code the modules needed by your application that do not already exist. +3. Create your app directory. In the app main file, import the module you need and instantiate the different stores. +4. Launch your blockchain. + +## Setup + +### Prerequisites + +- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed +- Don't forget to set your `PATH` and `GOPATH` + +### Setup work environment + +Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and: + +```bash +cd $GOPATH/src/github.com/your_username +git clone github.com/your_username/cosmos-sdk +cd cosmos-sdk +``` + +Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged: + +```bash +git remote add upstream github.com/cosmos/cosmos-sdk +git fetch upstream +git rebase upstream/master +``` + +We will also create a branch dedicated to our module: + +```bash +git checkout -b my_new_application +``` + +We are all set! + ## Application design ### Application description diff --git a/docs/sdk/sdk-by-examples/simple-governance/setup.md b/docs/sdk/sdk-by-examples/simple-governance/setup.md deleted file mode 100644 index 963c7bf81..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/setup.md +++ /dev/null @@ -1,32 +0,0 @@ -## Setup - -### Prerequisites - -- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed -- Don't forget to set your `PATH` and `GOPATH` - -### Setup work environment - -Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and: - -```bash -cd $GOPATH/src/github.com/your_username -git clone github.com/your_username/cosmos-sdk -cd cosmos-sdk -``` - -Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged: - -```bash -git remote add upstream github.com/cosmos/cosmos-sdk -git fetch upstream -git rebase upstream/master -``` - -We will also create a branch dedicated to our module: - -```bash -git checkout -b my_new_application -``` - -We are all set! \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/simple-gov-module.md b/docs/sdk/sdk-by-examples/simple-governance/simple-gov-module.md new file mode 100644 index 000000000..92687deef --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/simple-gov-module.md @@ -0,0 +1,316 @@ +# Simple Governance Module + +## Module initialization + +First, let us go into the module's folder and create a folder for our module. + +```bash +cd x/ +mkdir simple_governance +cd simple_governance +mkdir -p client/cli client/rest +touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go +``` + +Let us start by adding the files we will need. Your module's folder should look something like that: + +``` +x +└─── simple_governance + ├─── client + │ ├─── cli + │ │ └─── simple_governance.go + │ └─── rest + │ └─── simple_governance.go + ├─── errors.go + ├─── handler.go + ├─── keeper_keys.go + ├─── keeper.go + ├─── types.go + └─── codec.go +``` + +Let us go into the detail of each of these files. + +## Types + +**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)** + +In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section. + +For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in. + +Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type: + +- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method. +- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message. +- `GetSigners()`: Returns address(es) of the signer(s). +- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction. +- `Get()`: A basic getter, returns some property of the message. +- `String()`: Returns a human-readable version of the message + +For our simple governance messages, this means: + +- `Type()` will return `"simpleGov"` +- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit +- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative. +- As for other methods, less customization is required. You can check the code to see a standard way of implementing these. + +## Keeper + +**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)** + +### Short intro to keepers + +`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos. + + +As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that: + +```go +// in app.go + +// instanciate keepers +keeperA = moduleA.newKeeper(app.moduleAStoreKey) +keeperB = moduleB.newKeeper(app.moduleBStoreKey) + +// pass instance of keeperA to handler of module B +app.Router(). + AddRoute("moduleA", moduleA.NewHandler(keeperA)). + AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance +``` + +`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper. + +### Store for our app + +Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them. + +- `Proposals` will be indexed by `'proposals'|`. +- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'||'votes'|`. + +Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`. + +These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, )`. + +### Keepers for our app + +In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use. + +Now, let us try to think about which keeper from **external** modules our module's keepers need access to. +Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler. + +With all that in mind, we can define the structure of our `Keeper`: + +```go + type Keeper struct { + SimpleGov sdk.StoreKey // Key to our module's store + cdc *codec.Codec // Codec to encore/decode structs + ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance + sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store. + codespace sdk.CodespaceType // Reserves space for error codes + } +``` + +And the structure of our `KeeperRead`: + +```go +type KeeperRead struct { + Keeper +} +``` + +`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store. + +### Functions and Methods + +The first function we have to create is the constructor. + +```go +func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper +``` + +This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`. + +```go +func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead +``` + +Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level. + +*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers* + +Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`. + +- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read. +- `SetProposal`: Set a `Proposal` at index `'proposals'|`. Proposals need to be encoded to `byte` before they can be stored. +- `NewProposalID`: A function to generate a new unique `proposalID`. +- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`. +- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`. +- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period. + +The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error. + +*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*. + +## Handler + +**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)** + +### Constructor and core handlers + +Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function. + +In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`: + +```go +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case SubmitProposalMsg: + return handleSubmitProposalMsg(ctx, k, msg) + case VoteMsg: + return handleVoteMsg(ctx, k, msg) + default: + errMsg := "Unrecognized gov Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} +``` + +The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures: + +```go +func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result +``` + +Let us take a look at the parameters of this function: + +- The context `ctx` to access the stores. +- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case). +- The message `msg` that holds all the information provided by the sender of the transaction. + +The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info. + +### BeginBlocker and EndBlocker + +In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications. + +This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether. + +`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern): + +```go +func NewEndBlocker(k Keeper) sdk.EndBlocker { + return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + err := checkProposal(ctx, k) + if err != nil { + panic(err) + } + return + } +} +``` + +Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md). + +For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed: + +1. Get the oldest proposal from the `ProposalProcessingQueue` +2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit. +3. Check if proposal is accepted or rejected. Update the proposal status. +4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1. + +Let us perform a quick safety analysis on this process. +- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite +- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue. +- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`. + +## Codec + +**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)** + +The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare: + +```go +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil) + cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil) +} +``` +Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more). + +## Errors + +**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)** + +The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory. + +Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result. + +## Command-Line Interface + +**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)** + +Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module. + +The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra: + +```go + // Declare flags + const( + Flag = "flag" + ... + ) + + // Main command function. One function for each command. + func Command(codec *codec.Codec) *cobra.Command { + // Create the command to return + command := &cobra.Command{ + Use: "actual command", + Short: "Short description", + Run: func(cmd *cobra.Command, args []string) error { + // Actual function to run when command is used + }, + } + + // Add flags to the command + command.Flags().(FlagNameConstant, , "") + + return command + } +``` + +## Rest API + +**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)** + +The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**. + +________________________________________________________ + +USER INTERFACE <=======> REST SERVER <=======> FULL-NODE + +________________________________________________________ + +It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`. + +- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally. + +- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally. + +Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder. + +| Method | URL | Description | +|--------|---------------------------------|-------------------------------------------------------------| +| GET | /proposals | Range query to get all submitted proposals | +| POST | /proposals | Submit a new proposal | +| GET | /proposals/{id} | Returns a proposal given its ID | +| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal | +| POST | /proposals/{id}/votes | Cast a vote on a given proposal | +| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal | + +It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it. + +Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices. diff --git a/docs/sdk/sdk-by-examples/simple-governance/start.md b/docs/sdk/sdk-by-examples/simple-governance/start.md deleted file mode 100644 index 8461c84d9..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/start.md +++ /dev/null @@ -1,10 +0,0 @@ -## Starting your own project - -To get started, you just have to follow these simple steps: - -1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo -2. Code the modules needed by your application that do not already exist. -3. Create your app directory. In the app main file, import the module you need and instantiate the different stores. -4. Launch your blockchain. - -Easy as pie! With the introduction over, let us delve into practice and learn how to code a SDK application with an example. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md deleted file mode 100644 index 57571c151..000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md +++ /dev/null @@ -1,36 +0,0 @@ -## Submit a proposal - -Uuse the CLI to create a new proposal: - -```bash -simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms -``` - -Or, via a json file: - -```bash -simplegovcli propose --proposal="path/to/proposal.json" -``` - -Where proposal.json contains: - -```json -{ - "title": "Voting Period Update", - "description": "Should we change the proposal voting period to 3 weeks?", - "type": "Text", - "deposit": "300Atoms" -} -``` - -Get the details of your newly created proposal: - -```bash -simplegovcli proposal 1 -``` - -You can also check all the existing open proposals: - -```bash -simplegovcli proposals --active=true -``` \ No newline at end of file From a4690bbe58c688b885cd326b05133b479a770398 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 3 Oct 2018 14:09:35 -0400 Subject: [PATCH 11/15] Merge PR #2434: config.js --- docs/.vuepress/config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 32a71dd0c..7925697f5 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -15,8 +15,8 @@ module.exports = { collapsable: false, children: [ "/introduction/cosmos-hub", - "/introduction/tendermint-cosmos" - "/introduction/tendermint", + "/introduction/tendermint-cosmos", + "/introduction/tendermint" ] }, { From b4e8fe5e94c6cced7a74214c3838e6aa0d9acce2 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 3 Oct 2018 23:42:03 -0700 Subject: [PATCH 12/15] Merge PR #2435: Staking store keys bug fix + memory improvement Improve memory efficiency of getting store keys This is done by removing repeated appends, which will create a new slice if theres insufficient capacity, and instead creating a key of the correct size, and then copying the data into it. --- PENDING.md | 2 ++ x/stake/keeper/key.go | 63 +++++++++++++++++++++++--------------- x/stake/keeper/key_test.go | 35 +++++++++++++++++++++ 3 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 x/stake/keeper/key_test.go diff --git a/PENDING.md b/PENDING.md index b874b9bf7..70ba77d04 100644 --- a/PENDING.md +++ b/PENDING.md @@ -37,6 +37,7 @@ BREAKING CHANGES * `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub` * `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub` * [x/stake] [#1013] TendermintUpdates now uses transient store + * [x/stake] \#2435 Remove empty bytes from the ValidatorPowerRank store key * [x/gov] [#2195] Governance uses BFT Time * [x/gov] \#2256 Removed slashing for governance non-voting validators * [simulation] \#2162 Added back correct supply invariants @@ -129,6 +130,7 @@ IMPROVEMENTS * [x/auth] Signature verification's gas cost now accounts for pubkey type. [#2046](https://github.com/tendermint/tendermint/pull/2046) * [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) + * [x/stake] \#2435 Improve memory efficiency of getting the various store keys * [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 * \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`. diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 02d54e2b7..7d3ea0850 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -52,7 +52,7 @@ func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte { // VALUE: validator operator address ([]byte) func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte { // NOTE the address doesn't need to be stored because counter bytes must always be different - return getValidatorPowerRank(validator, pool) + return getValidatorPowerRank(validator) } // get the bonded validator index key for an operator address @@ -63,22 +63,23 @@ func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte { // get the power ranking of a validator // NOTE the larger values are of higher value // nolint: unparam -func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { +func getValidatorPowerRank(validator types.Validator) []byte { potentialPower := validator.Tokens powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first) + powerBytesLen := len(powerBytes) + // key is of format prefix || powerbytes || heightBytes || counterBytes + key := make([]byte, 1+powerBytesLen+8+2) - // heightBytes and counterBytes represent strings like powerBytes does - heightBytes := make([]byte, binary.MaxVarintLen64) - binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first) - counterBytes := make([]byte, 2) - binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority) + key[0] = ValidatorsByPowerIndexKey[0] + copy(key[1:powerBytesLen+1], powerBytes) - return append(append(append( - ValidatorsByPowerIndexKey, - powerBytes...), - heightBytes...), - counterBytes...) + // include heightBytes height is inverted (older validators first) + binary.BigEndian.PutUint64(key[powerBytesLen+1:powerBytesLen+9], ^uint64(validator.BondHeight)) + // include counterBytes, counter is inverted (first txns have priority) + binary.BigEndian.PutUint16(key[powerBytesLen+9:powerBytesLen+11], ^uint16(validator.BondIntraTxCounter)) + + return key } //______________________________________________________________________________ @@ -138,28 +139,42 @@ func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte { // gets the key for a redelegation // VALUE: stake/types.RedelegationKey func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { - return append(append( - GetREDsKey(delAddr.Bytes()), - valSrcAddr.Bytes()...), - valDstAddr.Bytes()...) + key := make([]byte, 1+sdk.AddrLen*3) + + copy(key[0:sdk.AddrLen+1], GetREDsKey(delAddr.Bytes())) + copy(key[sdk.AddrLen+1:2*sdk.AddrLen+1], valSrcAddr.Bytes()) + copy(key[2*sdk.AddrLen+1:3*sdk.AddrLen+1], valDstAddr.Bytes()) + + return key } // gets the index-key for a redelegation, stored by source-validator-index // VALUE: none (key rearrangement used) func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { - return append(append( - GetREDsFromValSrcIndexKey(valSrcAddr), - delAddr.Bytes()...), - valDstAddr.Bytes()...) + REDSFromValsSrcKey := GetREDsFromValSrcIndexKey(valSrcAddr) + offset := len(REDSFromValsSrcKey) + + // key is of the form REDSFromValsSrcKey || delAddr || valDstAddr + key := make([]byte, len(REDSFromValsSrcKey)+2*sdk.AddrLen) + copy(key[0:offset], REDSFromValsSrcKey) + copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) + copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valDstAddr.Bytes()) + return key } // gets the index-key for a redelegation, stored by destination-validator-index // VALUE: none (key rearrangement used) func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { - return append(append( - GetREDsToValDstIndexKey(valDstAddr), - delAddr.Bytes()...), - valSrcAddr.Bytes()...) + REDSToValsDstKey := GetREDsToValDstIndexKey(valDstAddr) + offset := len(REDSToValsDstKey) + + // key is of the form REDSToValsDstKey || delAddr || valSrcAddr + key := make([]byte, len(REDSToValsDstKey)+2*sdk.AddrLen) + copy(key[0:offset], REDSToValsDstKey) + copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) + copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valSrcAddr.Bytes()) + + return key } // rearranges the ValSrcIndexKey to get the REDKey diff --git a/x/stake/keeper/key_test.go b/x/stake/keeper/key_test.go new file mode 100644 index 000000000..3272a50e2 --- /dev/null +++ b/x/stake/keeper/key_test.go @@ -0,0 +1,35 @@ +package keeper + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +var ( + pk1 = ed25519.GenPrivKey().PubKey() +) + +func TestGetValidatorPowerRank(t *testing.T) { + valAddr1 := sdk.ValAddress(pk1.Bytes()) + emptyDesc := types.Description{} + val1 := types.NewValidator(valAddr1, pk1, emptyDesc) + val1.Tokens = sdk.NewDec(12) + + tests := []struct { + validator types.Validator + wantHex string + }{ + {val1, "05303030303030303030303132ffffffffffffffffffff"}, + } + for i, tt := range tests { + got := hex.EncodeToString(getValidatorPowerRank(tt.validator)) + + assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + } +} From 9f67e8af20ffa49aa50e12e1b711bb8d3c7e0251 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 4 Oct 2018 19:00:24 +0800 Subject: [PATCH 13/15] Merge PR #2215: Add swagger-ui for gaiacli lite-server --- .circleci/config.yml | 2 +- .gitignore | 1 + Gopkg.lock | 9 + Gopkg.toml | 4 + Makefile | 13 +- PENDING.md | 1 + client/context/broadcast.go | 16 + client/context/context.go | 2 + client/flags.go | 39 +- client/lcd/lcd_test.go | 7 +- client/lcd/root.go | 14 +- client/lcd/statik/init.go | 3 + client/lcd/swagger-ui/favicon-16x16.png | Bin 0 -> 445 bytes client/lcd/swagger-ui/favicon-32x32.png | Bin 0 -> 1141 bytes client/lcd/swagger-ui/index.html | 60 + client/lcd/swagger-ui/oauth2-redirect.html | 67 + client/lcd/swagger-ui/swagger-ui-bundle.js | 41776 ++++++++++++++++ .../lcd/swagger-ui/swagger-ui-bundle.js.map | 1 + .../swagger-ui-standalone-preset.js | 13173 +++++ .../swagger-ui-standalone-preset.js.map | 1 + client/lcd/swagger-ui/swagger-ui.css | 3 + client/lcd/swagger-ui/swagger-ui.css.map | 1 + client/lcd/swagger-ui/swagger-ui.js | 25501 ++++++++++ client/lcd/swagger-ui/swagger-ui.js.map | 1 + client/lcd/swagger-ui/swagger.yaml | 704 + client/rpc/block.go | 14 +- client/rpc/status.go | 24 +- client/rpc/validators.go | 14 +- client/tx/broadcast.go | 59 +- client/tx/query.go | 9 +- client/tx/root.go | 3 +- client/tx/search.go | 18 +- client/utils/rest.go | 31 +- cmd/gaia/cmd/gaiacli/main.go | 1 + docs/DOCS_README.md | 20 + docs/clients/lcd-rest-api.yaml | 936 - docs/light/readme.md | 1 + examples/basecoin/cmd/basecli/main.go | 1 + tools/Makefile | 23 + tools/gometalinter.json | 2 +- x/auth/client/cli/account.go | 7 +- x/auth/client/cli/sign.go | 7 +- x/auth/client/rest/query.go | 45 +- x/auth/client/rest/sign.go | 7 +- x/bank/client/rest/broadcast.go | 7 +- x/bank/client/rest/sendtx.go | 2 +- 46 files changed, 81580 insertions(+), 1050 deletions(-) create mode 100644 client/lcd/statik/init.go create mode 100644 client/lcd/swagger-ui/favicon-16x16.png create mode 100644 client/lcd/swagger-ui/favicon-32x32.png create mode 100644 client/lcd/swagger-ui/index.html create mode 100644 client/lcd/swagger-ui/oauth2-redirect.html create mode 100644 client/lcd/swagger-ui/swagger-ui-bundle.js create mode 100644 client/lcd/swagger-ui/swagger-ui-bundle.js.map create mode 100644 client/lcd/swagger-ui/swagger-ui-standalone-preset.js create mode 100644 client/lcd/swagger-ui/swagger-ui-standalone-preset.js.map create mode 100644 client/lcd/swagger-ui/swagger-ui.css create mode 100644 client/lcd/swagger-ui/swagger-ui.css.map create mode 100644 client/lcd/swagger-ui/swagger-ui.js create mode 100644 client/lcd/swagger-ui/swagger-ui.js.map create mode 100644 client/lcd/swagger-ui/swagger.yaml delete mode 100644 docs/clients/lcd-rest-api.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index a62c61fd3..c2b3bbdd1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: /go/src/github.com/cosmos/cosmos-sdk docker: - - image: circleci/golang:1.10.3 + - image: circleci/golang:1.11.1 environment: GOBIN: /tmp/workspace/bin diff --git a/.gitignore b/.gitignore index 8498547cc..c6713cae8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ docs/_build examples/basecoin/app/data baseapp/data/* client/lcd/keys/* +client/lcd/statik/statik.go mytestnet # Testing diff --git a/Gopkg.lock b/Gopkg.lock index 62f1f7e5b..c3b08bd5c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -313,6 +313,14 @@ pruneopts = "UT" revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2" +[[projects]] + digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64" + name = "github.com/rakyll/statik" + packages = ["fs"] + pruneopts = "UT" + revision = "aa8a7b1baecd0f31a436bf7956fcdcc609a83035" + version = "v0.1.4" + [[projects]] digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" name = "github.com/rcrowley/go-metrics" @@ -651,6 +659,7 @@ "github.com/mitchellh/go-homedir", "github.com/pelletier/go-toml", "github.com/pkg/errors", + "github.com/rakyll/statik/fs", "github.com/spf13/cobra", "github.com/spf13/pflag", "github.com/spf13/viper", diff --git a/Gopkg.toml b/Gopkg.toml index ca9cac0d8..45ffc8317 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -67,6 +67,10 @@ name = "github.com/zondax/ledger-goclient" version = "=v0.1.0" +[[constraint]] + name = "github.com/rakyll/statik" + version = "=v0.1.4" + [prune] go-tests = true unused-packages = true diff --git a/Makefile b/Makefile index 3e0420072..acf81fc97 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ TMP_BUILD_TAGS := $(BUILD_TAGS) BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) endif -build: check-ledger +build: check-ledger update_gaia_lite_docs ifeq ($(OS),Windows_NT) go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli @@ -44,6 +44,9 @@ endif build-linux: LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build +update_gaia_lite_docs: + @statik -src=client/lcd/swagger-ui -dest=client/lcd -f + build_cosmos-sdk-cli: ifeq ($(OS),Windows_NT) go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli @@ -64,7 +67,7 @@ else go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli endif -install: check-ledger +install: check-ledger update_gaia_lite_docs go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli @@ -179,14 +182,14 @@ test_cover: test_lint: gometalinter.v2 --config=tools/gometalinter.json ./... - !(gometalinter.v2 --exclude /usr/lib/go/src/ --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") + !(gometalinter.v2 --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s dep status >> /dev/null !(grep -n branch Gopkg.toml) format: - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -w -s - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w benchmark: @go test -bench=. $(PACKAGES_NOSIMULATION) diff --git a/PENDING.md b/PENDING.md index 70ba77d04..ca77f2ad4 100644 --- a/PENDING.md +++ b/PENDING.md @@ -77,6 +77,7 @@ FEATURES * [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions * [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`. * [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint. + * [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers` * Gaia CLI (`gaiacli`) * [cli] Cmds to query staking pool and params diff --git a/client/context/broadcast.go b/client/context/broadcast.go index 1240ffa02..b15b2a8e3 100644 --- a/client/context/broadcast.go +++ b/client/context/broadcast.go @@ -69,6 +69,22 @@ func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (*ctypes.ResultBroadc return res, err } +// BroadcastTxSync broadcasts transaction bytes to a Tendermint node +// synchronously. +func (ctx CLIContext) BroadcastTxSync(tx []byte) (*ctypes.ResultBroadcastTx, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, err + } + + res, err := node.BroadcastTxSync(tx) + if err != nil { + return res, err + } + + return res, err +} + // BroadcastTxAsync broadcasts transaction bytes to a Tendermint node // asynchronously. func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) { diff --git a/client/context/context.go b/client/context/context.go index 4f106d857..ce36c37a8 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -50,6 +50,7 @@ type CLIContext struct { GenerateOnly bool fromAddress types.AccAddress fromName string + Indent bool } // NewCLIContext returns a new initialized CLIContext with parameters from the @@ -87,6 +88,7 @@ func NewCLIContext() CLIContext { GenerateOnly: viper.GetBool(client.FlagGenerateOnly), fromAddress: fromAddress, fromName: fromName, + Indent: viper.GetBool(client.FlagIndentResponse), } } diff --git a/client/flags.go b/client/flags.go index ff9354937..9d03939e0 100644 --- a/client/flags.go +++ b/client/flags.go @@ -17,24 +17,25 @@ const ( DefaultGasLimit = 200000 GasFlagSimulate = "simulate" - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagHeight = "height" - FlagGas = "gas" - FlagGasAdjustment = "gas-adjustment" - FlagTrustNode = "trust-node" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagMemo = "memo" - FlagFee = "fee" - FlagAsync = "async" - FlagJson = "json" - FlagPrintResponse = "print-response" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagGas = "gas" + FlagGasAdjustment = "gas-adjustment" + FlagTrustNode = "trust-node" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagMemo = "memo" + FlagFee = "fee" + FlagAsync = "async" + FlagJson = "json" + FlagPrintResponse = "print-response" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagIndentResponse = "indent" ) // LineBreak can be included in a command list to provide a blank line @@ -47,6 +48,7 @@ var ( // GetCommands adds common flags to query commands func GetCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { + c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") @@ -63,6 +65,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { // PostCommands adds common flags for commands to post tx func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { + c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 192b99a8f..485c5a2f6 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -372,8 +372,9 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { // broadcast tx broadcastPayload := struct { - Tx auth.StdTx `json:"tx"` - }{Tx: signedMsg} + Tx auth.StdTx `json:"tx"` + Return string `json:"return"` + }{Tx: signedMsg, Return: "block"} json, err = cdc.MarshalJSON(broadcastPayload) require.Nil(t, err) res, body = Request(t, port, "POST", "/tx/broadcast", json) @@ -851,7 +852,7 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc } }`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence)) - res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr) + res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers%v", receiveAddr, queryStr), jsonStr) return } diff --git a/client/lcd/root.go b/client/lcd/root.go index 4bb7d9e17..ef690ed55 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -18,6 +18,7 @@ import ( slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" "github.com/spf13/cobra" "github.com/spf13/viper" cmn "github.com/tendermint/tendermint/libs/common" @@ -46,6 +47,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) (err error) { listenAddr := viper.GetString(flagListenAddr) handler := createHandler(cdc) + registerSwaggerUI(handler) logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") maxOpen := viper.GetInt(flagMaxOpenConnections) sslHosts := viper.GetString(flagSSLHosts) @@ -121,6 +123,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) @@ -128,7 +131,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { return cmd } -func createHandler(cdc *codec.Codec) http.Handler { +func createHandler(cdc *codec.Codec) *mux.Router { r := mux.NewRouter() kb, err := keys.GetKeyBase() //XXX @@ -154,6 +157,15 @@ func createHandler(cdc *codec.Codec) http.Handler { return r } +func registerSwaggerUI(r *mux.Router) { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + staticServer := http.FileServer(statikFS) + r.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) +} + func validateCertKeyFiles(certFile, keyFile string) error { if keyFile == "" { return errors.New("a key file is required") diff --git a/client/lcd/statik/init.go b/client/lcd/statik/init.go new file mode 100644 index 000000000..9633aeb29 --- /dev/null +++ b/client/lcd/statik/init.go @@ -0,0 +1,3 @@ +package statik + +//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/lcd/statik diff --git a/client/lcd/swagger-ui/favicon-16x16.png b/client/lcd/swagger-ui/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0f7e13b0d9903d27a9129950b1dad362361504e4 GIT binary patch literal 445 zcmV;u0Yd(XP)rNm2=6wQ7&2F}_`h_PI>(9Fx!5<0%l6W{u0OQ#*rglqx3__&vD?|#%fhn*Mn&YY1i+JQHqPvZ34FR@_E%P@x zzTL;Bw#nJXWY}D7^bC>-bx{t|^|R6Oci&MKvov8Op~S=}R=h^p-=vZ0uqG@LE6tP7 n92{cY$^db6>&z__iT?Z#Z8BG|DVcT0DjiaEd>Z!7_`J}8! zKk_$1lGm$vJOY&DjT-(&VGn0;R`iN9=1aOuG`H}BlY>&R3KbGER zB2$7euhH;y1C_LTQex%L6khZpkjFn!ajOUK)f3JLz+I;CE@(N)T)CM4AWjfl-(04= zrsMQ)#NG6nr^Y7!6LA;iHXh?UOFE%hhy>7dl=;I$J>g0BH_r|_4ctEsXx z2sDIQnwa*rcK=*3XUC$D{I@}DTNs@GCb7dB2%%nV%jR){xktt;Ah09op7x@l5D6B2 z0uBdt0YmcN!o?lMpu9Io(1&B1s{TUu*a>2&>Iycx__fbDRM8PYtLt+#G*xSt(cn}K zt!~W2{`9r)xkh^xodLS&FbYw`x$t&Vhl?)#f&k-lZIs<`$gTj{^#^HewuJz(WnUZZ z{Ty_aE;^93bhc-^^k6ZM!^e~$q5!Zz`XPta{a@651gPzaFx$&%IHL6hx$mSeAa#n6 zLkyc-M zs$qhBZhCNE^aIEV)H_~^IeqSRnvo!21Qc`Z;S9!IqXl4K(RUImejotzuG65LVuGS# zcqp@OA8~ln^4c^VihUew)IOX^E9KMtvSvnZ| zC@rl{f(B*PA26aFR`|X!!I(7x_|kq{rlqwhCia+CfNbOg_yYt0bDCc4g#h#`3jpCd zNAhr%4#Ye{i>ni$fzY%r0IS%l3HHZ4tTjOi=JW-t_iG~)oC!2C!52Cc|TAPaH zJ}l%m9yPmA-4#lJea@uf$a`(1;={rL2f*8;7%icbF}e^_`X#ndU=SI0nIn8hXPXHS zSN4rbF}jl0HWx(_`q`-SRa9jP8Ab!}sThNkQ634k=qXBVM4`o{M>qrLJD ze*%D)S;wpxG$d%FcDf-6%zMqWA+gw!C1~T5+|ys$G3Ksm&x59Lyd?0l+LWSk6hc4~ z+yC>|4f;X3#cq3!)>#Mvb-^co7LMrzqWeKB$21I>tJgaGFwu6eB%&j?@d*8GAx~In zI1p-lXVKtcvY7;$TX~wjYw|QhB%q!npQES%F~%Aqz~pJB%rNu!xAj;>xZt75!VHju zfFy%B-`3;Qf<{h94~I62zcHv}D5pS-QCN`M8K1>jN9mpbrFk=5no8j!00000NkvXX Hu0mjfOavUK literal 0 HcmV?d00001 diff --git a/client/lcd/swagger-ui/index.html b/client/lcd/swagger-ui/index.html new file mode 100644 index 000000000..0c4855763 --- /dev/null +++ b/client/lcd/swagger-ui/index.html @@ -0,0 +1,60 @@ + + + + + + Swagger UI + + + + + + + +
+ + + + + + diff --git a/client/lcd/swagger-ui/oauth2-redirect.html b/client/lcd/swagger-ui/oauth2-redirect.html new file mode 100644 index 000000000..fb68399d2 --- /dev/null +++ b/client/lcd/swagger-ui/oauth2-redirect.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/client/lcd/swagger-ui/swagger-ui-bundle.js b/client/lcd/swagger-ui/swagger-ui-bundle.js new file mode 100644 index 000000000..4491b4b28 --- /dev/null +++ b/client/lcd/swagger-ui/swagger-ui-bundle.js @@ -0,0 +1,41776 @@ +! function(e, t) { + "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.SwaggerUIBundle = t() : e.SwaggerUIBundle = t() +}(this, function() { + return function(e) { + var t = {}; + + function n(r) { + if (t[r]) return t[r].exports; + var o = t[r] = { + i: r, + l: !1, + exports: {} + }; + return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports + } + return n.m = e, n.c = t, n.d = function(e, t, r) { + n.o(e, t) || Object.defineProperty(e, t, { + configurable: !1, + enumerable: !0, + get: r + }) + }, n.n = function(e) { + var t = e && e.__esModule ? function() { + return e.default + } : function() { + return e + }; + return n.d(t, "a", t), t + }, n.o = function(e, t) { + return Object.prototype.hasOwnProperty.call(e, t) + }, n.p = "/dist", n(n.s = 445) + }([function(e, t, n) { + "use strict"; + e.exports = n(75) + }, function(e, t, n) { + e.exports = n(853)() + }, function(e, t, n) { + "use strict"; + t.__esModule = !0, t.default = function(e, t) { + if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") + } + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r, o = n(262), + i = (r = o) && r.__esModule ? r : { + default: r + }; + t.default = function() { + function e(e, t) { + for (var n = 0; n < t.length; n++) { + var r = t[n]; + r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), (0, i.default)(e, r.key, r) + } + } + return function(t, n, r) { + return n && e(t.prototype, n), r && e(t, r), t + } + }() + }, function(e, t, n) { + e.exports = { + default: n(766), + __esModule: !0 + } + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r, o = n(45), + i = (r = o) && r.__esModule ? r : { + default: r + }; + t.default = function(e, t) { + if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return !t || "object" !== (void 0 === t ? "undefined" : (0, i.default)(t)) && "function" != typeof t ? e : t + } + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r = a(n(768)), + o = a(n(349)), + i = a(n(45)); + + function a(e) { + return e && e.__esModule ? e : { + default: e + } + } + t.default = function(e, t) { + if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + (void 0 === t ? "undefined" : (0, i.default)(t))); + e.prototype = (0, o.default)(t && t.prototype, { + constructor: { + value: e, + enumerable: !1, + writable: !0, + configurable: !0 + } + }), t && (r.default ? (0, r.default)(e, t) : e.__proto__ = t) + } + }, function(e, t, n) { + var r; + r = function() { + "use strict"; + var e = Array.prototype.slice; + + function t(e, t) { + t && (e.prototype = Object.create(t.prototype)), e.prototype.constructor = e + } + + function n(e) { + return a(e) ? e : J(e) + } + + function r(e) { + return u(e) ? e : Y(e) + } + + function o(e) { + return s(e) ? e : K(e) + } + + function i(e) { + return a(e) && !l(e) ? e : G(e) + } + + function a(e) { + return !(!e || !e[f]) + } + + function u(e) { + return !(!e || !e[p]) + } + + function s(e) { + return !(!e || !e[d]) + } + + function l(e) { + return u(e) || s(e) + } + + function c(e) { + return !(!e || !e[h]) + } + t(r, n), t(o, n), t(i, n), n.isIterable = a, n.isKeyed = u, n.isIndexed = s, n.isAssociative = l, n.isOrdered = c, n.Keyed = r, n.Indexed = o, n.Set = i; + var f = "@@__IMMUTABLE_ITERABLE__@@", + p = "@@__IMMUTABLE_KEYED__@@", + d = "@@__IMMUTABLE_INDEXED__@@", + h = "@@__IMMUTABLE_ORDERED__@@", + v = 5, + m = 1 << v, + g = m - 1, + y = {}, + b = { + value: !1 + }, + _ = { + value: !1 + }; + + function w(e) { + return e.value = !1, e + } + + function E(e) { + e && (e.value = !0) + } + + function x() {} + + function S(e, t) { + t = t || 0; + for (var n = Math.max(0, e.length - t), r = new Array(n), o = 0; o < n; o++) r[o] = e[o + t]; + return r + } + + function C(e) { + return void 0 === e.size && (e.size = e.__iterate(A)), e.size + } + + function k(e, t) { + if ("number" != typeof t) { + var n = t >>> 0; + if ("" + n !== t || 4294967295 === n) return NaN; + t = n + } + return t < 0 ? C(e) + t : t + } + + function A() { + return !0 + } + + function O(e, t, n) { + return (0 === e || void 0 !== n && e <= -n) && (void 0 === t || void 0 !== n && t >= n) + } + + function P(e, t) { + return M(e, t, 0) + } + + function T(e, t) { + return M(e, t, t) + } + + function M(e, t, n) { + return void 0 === e ? n : e < 0 ? Math.max(0, t + e) : void 0 === t ? e : Math.min(t, e) + } + var I = 0, + j = 1, + N = 2, + R = "function" == typeof Symbol && Symbol.iterator, + D = "@@iterator", + L = R || D; + + function U(e) { + this.next = e + } + + function q(e, t, n, r) { + var o = 0 === e ? t : 1 === e ? n : [t, n]; + return r ? r.value = o : r = { + value: o, + done: !1 + }, r + } + + function F() { + return { + value: void 0, + done: !0 + } + } + + function z(e) { + return !!H(e) + } + + function B(e) { + return e && "function" == typeof e.next + } + + function V(e) { + var t = H(e); + return t && t.call(e) + } + + function H(e) { + var t = e && (R && e[R] || e[D]); + if ("function" == typeof t) return t + } + + function W(e) { + return e && "number" == typeof e.length + } + + function J(e) { + return null === e || void 0 === e ? ie() : a(e) ? e.toSeq() : function(e) { + var t = se(e) || "object" == typeof e && new te(e); + if (!t) throw new TypeError("Expected Array or iterable object of values, or keyed object: " + e); + return t + }(e) + } + + function Y(e) { + return null === e || void 0 === e ? ie().toKeyedSeq() : a(e) ? u(e) ? e.toSeq() : e.fromEntrySeq() : ae(e) + } + + function K(e) { + return null === e || void 0 === e ? ie() : a(e) ? u(e) ? e.entrySeq() : e.toIndexedSeq() : ue(e) + } + + function G(e) { + return (null === e || void 0 === e ? ie() : a(e) ? u(e) ? e.entrySeq() : e : ue(e)).toSetSeq() + } + U.prototype.toString = function() { + return "[Iterator]" + }, U.KEYS = I, U.VALUES = j, U.ENTRIES = N, U.prototype.inspect = U.prototype.toSource = function() { + return this.toString() + }, U.prototype[L] = function() { + return this + }, t(J, n), J.of = function() { + return J(arguments) + }, J.prototype.toSeq = function() { + return this + }, J.prototype.toString = function() { + return this.__toString("Seq {", "}") + }, J.prototype.cacheResult = function() { + return !this._cache && this.__iterateUncached && (this._cache = this.entrySeq().toArray(), this.size = this._cache.length), this + }, J.prototype.__iterate = function(e, t) { + return le(this, e, t, !0) + }, J.prototype.__iterator = function(e, t) { + return ce(this, e, t, !0) + }, t(Y, J), Y.prototype.toKeyedSeq = function() { + return this + }, t(K, J), K.of = function() { + return K(arguments) + }, K.prototype.toIndexedSeq = function() { + return this + }, K.prototype.toString = function() { + return this.__toString("Seq [", "]") + }, K.prototype.__iterate = function(e, t) { + return le(this, e, t, !1) + }, K.prototype.__iterator = function(e, t) { + return ce(this, e, t, !1) + }, t(G, J), G.of = function() { + return G(arguments) + }, G.prototype.toSetSeq = function() { + return this + }, J.isSeq = oe, J.Keyed = Y, J.Set = G, J.Indexed = K; + var $, Z, X, Q = "@@__IMMUTABLE_SEQ__@@"; + + function ee(e) { + this._array = e, this.size = e.length + } + + function te(e) { + var t = Object.keys(e); + this._object = e, this._keys = t, this.size = t.length + } + + function ne(e) { + this._iterable = e, this.size = e.length || e.size + } + + function re(e) { + this._iterator = e, this._iteratorCache = [] + } + + function oe(e) { + return !(!e || !e[Q]) + } + + function ie() { + return $ || ($ = new ee([])) + } + + function ae(e) { + var t = Array.isArray(e) ? new ee(e).fromEntrySeq() : B(e) ? new re(e).fromEntrySeq() : z(e) ? new ne(e).fromEntrySeq() : "object" == typeof e ? new te(e) : void 0; + if (!t) throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: " + e); + return t + } + + function ue(e) { + var t = se(e); + if (!t) throw new TypeError("Expected Array or iterable object of values: " + e); + return t + } + + function se(e) { + return W(e) ? new ee(e) : B(e) ? new re(e) : z(e) ? new ne(e) : void 0 + } + + function le(e, t, n, r) { + var o = e._cache; + if (o) { + for (var i = o.length - 1, a = 0; a <= i; a++) { + var u = o[n ? i - a : a]; + if (!1 === t(u[1], r ? u[0] : a, e)) return a + 1 + } + return a + } + return e.__iterateUncached(t, n) + } + + function ce(e, t, n, r) { + var o = e._cache; + if (o) { + var i = o.length - 1, + a = 0; + return new U(function() { + var e = o[n ? i - a : a]; + return a++ > i ? { + value: void 0, + done: !0 + } : q(t, r ? e[0] : a - 1, e[1]) + }) + } + return e.__iteratorUncached(t, n) + } + + function fe(e, t) { + return t ? function e(t, n, r, o) { + if (Array.isArray(n)) return t.call(o, r, K(n).map(function(r, o) { + return e(t, r, o, n) + })); + if (de(n)) return t.call(o, r, Y(n).map(function(r, o) { + return e(t, r, o, n) + })); + return n + }(t, e, "", { + "": e + }) : pe(e) + } + + function pe(e) { + return Array.isArray(e) ? K(e).map(pe).toList() : de(e) ? Y(e).map(pe).toMap() : e + } + + function de(e) { + return e && (e.constructor === Object || void 0 === e.constructor) + } + + function he(e, t) { + if (e === t || e != e && t != t) return !0; + if (!e || !t) return !1; + if ("function" == typeof e.valueOf && "function" == typeof t.valueOf) { + if ((e = e.valueOf()) === (t = t.valueOf()) || e != e && t != t) return !0; + if (!e || !t) return !1 + } + return !("function" != typeof e.equals || "function" != typeof t.equals || !e.equals(t)) + } + + function ve(e, t) { + if (e === t) return !0; + if (!a(t) || void 0 !== e.size && void 0 !== t.size && e.size !== t.size || void 0 !== e.__hash && void 0 !== t.__hash && e.__hash !== t.__hash || u(e) !== u(t) || s(e) !== s(t) || c(e) !== c(t)) return !1; + if (0 === e.size && 0 === t.size) return !0; + var n = !l(e); + if (c(e)) { + var r = e.entries(); + return t.every(function(e, t) { + var o = r.next().value; + return o && he(o[1], e) && (n || he(o[0], t)) + }) && r.next().done + } + var o = !1; + if (void 0 === e.size) + if (void 0 === t.size) "function" == typeof e.cacheResult && e.cacheResult(); + else { + o = !0; + var i = e; + e = t, t = i + } + var f = !0, + p = t.__iterate(function(t, r) { + if (n ? !e.has(t) : o ? !he(t, e.get(r, y)) : !he(e.get(r, y), t)) return f = !1, !1 + }); + return f && e.size === p + } + + function me(e, t) { + if (!(this instanceof me)) return new me(e, t); + if (this._value = e, this.size = void 0 === t ? 1 / 0 : Math.max(0, t), 0 === this.size) { + if (Z) return Z; + Z = this + } + } + + function ge(e, t) { + if (!e) throw new Error(t) + } + + function ye(e, t, n) { + if (!(this instanceof ye)) return new ye(e, t, n); + if (ge(0 !== n, "Cannot step a Range by 0"), e = e || 0, void 0 === t && (t = 1 / 0), n = void 0 === n ? 1 : Math.abs(n), t < e && (n = -n), this._start = e, this._end = t, this._step = n, this.size = Math.max(0, Math.ceil((t - e) / n - 1) + 1), 0 === this.size) { + if (X) return X; + X = this + } + } + + function be() { + throw TypeError("Abstract") + } + + function _e() {} + + function we() {} + + function Ee() {} + J.prototype[Q] = !0, t(ee, K), ee.prototype.get = function(e, t) { + return this.has(e) ? this._array[k(this, e)] : t + }, ee.prototype.__iterate = function(e, t) { + for (var n = this._array, r = n.length - 1, o = 0; o <= r; o++) + if (!1 === e(n[t ? r - o : o], o, this)) return o + 1; + return o + }, ee.prototype.__iterator = function(e, t) { + var n = this._array, + r = n.length - 1, + o = 0; + return new U(function() { + return o > r ? { + value: void 0, + done: !0 + } : q(e, o, n[t ? r - o++ : o++]) + }) + }, t(te, Y), te.prototype.get = function(e, t) { + return void 0 === t || this.has(e) ? this._object[e] : t + }, te.prototype.has = function(e) { + return this._object.hasOwnProperty(e) + }, te.prototype.__iterate = function(e, t) { + for (var n = this._object, r = this._keys, o = r.length - 1, i = 0; i <= o; i++) { + var a = r[t ? o - i : i]; + if (!1 === e(n[a], a, this)) return i + 1 + } + return i + }, te.prototype.__iterator = function(e, t) { + var n = this._object, + r = this._keys, + o = r.length - 1, + i = 0; + return new U(function() { + var a = r[t ? o - i : i]; + return i++ > o ? { + value: void 0, + done: !0 + } : q(e, a, n[a]) + }) + }, te.prototype[h] = !0, t(ne, K), ne.prototype.__iterateUncached = function(e, t) { + if (t) return this.cacheResult().__iterate(e, t); + var n = V(this._iterable), + r = 0; + if (B(n)) + for (var o; !(o = n.next()).done && !1 !== e(o.value, r++, this);); + return r + }, ne.prototype.__iteratorUncached = function(e, t) { + if (t) return this.cacheResult().__iterator(e, t); + var n = V(this._iterable); + if (!B(n)) return new U(F); + var r = 0; + return new U(function() { + var t = n.next(); + return t.done ? t : q(e, r++, t.value) + }) + }, t(re, K), re.prototype.__iterateUncached = function(e, t) { + if (t) return this.cacheResult().__iterate(e, t); + for (var n, r = this._iterator, o = this._iteratorCache, i = 0; i < o.length;) + if (!1 === e(o[i], i++, this)) return i; + for (; !(n = r.next()).done;) { + var a = n.value; + if (o[i] = a, !1 === e(a, i++, this)) break + } + return i + }, re.prototype.__iteratorUncached = function(e, t) { + if (t) return this.cacheResult().__iterator(e, t); + var n = this._iterator, + r = this._iteratorCache, + o = 0; + return new U(function() { + if (o >= r.length) { + var t = n.next(); + if (t.done) return t; + r[o] = t.value + } + return q(e, o, r[o++]) + }) + }, t(me, K), me.prototype.toString = function() { + return 0 === this.size ? "Repeat []" : "Repeat [ " + this._value + " " + this.size + " times ]" + }, me.prototype.get = function(e, t) { + return this.has(e) ? this._value : t + }, me.prototype.includes = function(e) { + return he(this._value, e) + }, me.prototype.slice = function(e, t) { + var n = this.size; + return O(e, t, n) ? this : new me(this._value, T(t, n) - P(e, n)) + }, me.prototype.reverse = function() { + return this + }, me.prototype.indexOf = function(e) { + return he(this._value, e) ? 0 : -1 + }, me.prototype.lastIndexOf = function(e) { + return he(this._value, e) ? this.size : -1 + }, me.prototype.__iterate = function(e, t) { + for (var n = 0; n < this.size; n++) + if (!1 === e(this._value, n, this)) return n + 1; + return n + }, me.prototype.__iterator = function(e, t) { + var n = this, + r = 0; + return new U(function() { + return r < n.size ? q(e, r++, n._value) : { + value: void 0, + done: !0 + } + }) + }, me.prototype.equals = function(e) { + return e instanceof me ? he(this._value, e._value) : ve(e) + }, t(ye, K), ye.prototype.toString = function() { + return 0 === this.size ? "Range []" : "Range [ " + this._start + "..." + this._end + (1 !== this._step ? " by " + this._step : "") + " ]" + }, ye.prototype.get = function(e, t) { + return this.has(e) ? this._start + k(this, e) * this._step : t + }, ye.prototype.includes = function(e) { + var t = (e - this._start) / this._step; + return t >= 0 && t < this.size && t === Math.floor(t) + }, ye.prototype.slice = function(e, t) { + return O(e, t, this.size) ? this : (e = P(e, this.size), (t = T(t, this.size)) <= e ? new ye(0, 0) : new ye(this.get(e, this._end), this.get(t, this._end), this._step)) + }, ye.prototype.indexOf = function(e) { + var t = e - this._start; + if (t % this._step == 0) { + var n = t / this._step; + if (n >= 0 && n < this.size) return n + } + return -1 + }, ye.prototype.lastIndexOf = function(e) { + return this.indexOf(e) + }, ye.prototype.__iterate = function(e, t) { + for (var n = this.size - 1, r = this._step, o = t ? this._start + n * r : this._start, i = 0; i <= n; i++) { + if (!1 === e(o, i, this)) return i + 1; + o += t ? -r : r + } + return i + }, ye.prototype.__iterator = function(e, t) { + var n = this.size - 1, + r = this._step, + o = t ? this._start + n * r : this._start, + i = 0; + return new U(function() { + var a = o; + return o += t ? -r : r, i > n ? { + value: void 0, + done: !0 + } : q(e, i++, a) + }) + }, ye.prototype.equals = function(e) { + return e instanceof ye ? this._start === e._start && this._end === e._end && this._step === e._step : ve(this, e) + }, t(be, n), t(_e, be), t(we, be), t(Ee, be), be.Keyed = _e, be.Indexed = we, be.Set = Ee; + var xe = "function" == typeof Math.imul && -2 === Math.imul(4294967295, 2) ? Math.imul : function(e, t) { + var n = 65535 & (e |= 0), + r = 65535 & (t |= 0); + return n * r + ((e >>> 16) * r + n * (t >>> 16) << 16 >>> 0) | 0 + }; + + function Se(e) { + return e >>> 1 & 1073741824 | 3221225471 & e + } + + function Ce(e) { + if (!1 === e || null === e || void 0 === e) return 0; + if ("function" == typeof e.valueOf && (!1 === (e = e.valueOf()) || null === e || void 0 === e)) return 0; + if (!0 === e) return 1; + var t = typeof e; + if ("number" === t) { + if (e != e || e === 1 / 0) return 0; + var n = 0 | e; + for (n !== e && (n ^= 4294967295 * e); e > 4294967295;) n ^= e /= 4294967295; + return Se(n) + } + if ("string" === t) return e.length > je ? function(e) { + var t = De[e]; + void 0 === t && (t = ke(e), Re === Ne && (Re = 0, De = {}), Re++, De[e] = t); + return t + }(e) : ke(e); + if ("function" == typeof e.hashCode) return e.hashCode(); + if ("object" === t) return function(e) { + var t; + if (Te && void 0 !== (t = Pe.get(e))) return t; + if (void 0 !== (t = e[Ie])) return t; + if (!Oe) { + if (void 0 !== (t = e.propertyIsEnumerable && e.propertyIsEnumerable[Ie])) return t; + if (void 0 !== (t = function(e) { + if (e && e.nodeType > 0) switch (e.nodeType) { + case 1: + return e.uniqueID; + case 9: + return e.documentElement && e.documentElement.uniqueID + } + }(e))) return t + } + t = ++Me, 1073741824 & Me && (Me = 0); + if (Te) Pe.set(e, t); + else { + if (void 0 !== Ae && !1 === Ae(e)) throw new Error("Non-extensible objects are not allowed as keys."); + if (Oe) Object.defineProperty(e, Ie, { + enumerable: !1, + configurable: !1, + writable: !1, + value: t + }); + else if (void 0 !== e.propertyIsEnumerable && e.propertyIsEnumerable === e.constructor.prototype.propertyIsEnumerable) e.propertyIsEnumerable = function() { + return this.constructor.prototype.propertyIsEnumerable.apply(this, arguments) + }, e.propertyIsEnumerable[Ie] = t; + else { + if (void 0 === e.nodeType) throw new Error("Unable to set a non-enumerable property on object."); + e[Ie] = t + } + } + return t + }(e); + if ("function" == typeof e.toString) return ke(e.toString()); + throw new Error("Value type " + t + " cannot be hashed.") + } + + function ke(e) { + for (var t = 0, n = 0; n < e.length; n++) t = 31 * t + e.charCodeAt(n) | 0; + return Se(t) + } + var Ae = Object.isExtensible, + Oe = function() { + try { + return Object.defineProperty({}, "@", {}), !0 + } catch (e) { + return !1 + } + }(); + var Pe, Te = "function" == typeof WeakMap; + Te && (Pe = new WeakMap); + var Me = 0, + Ie = "__immutablehash__"; + "function" == typeof Symbol && (Ie = Symbol(Ie)); + var je = 16, + Ne = 255, + Re = 0, + De = {}; + + function Le(e) { + ge(e !== 1 / 0, "Cannot perform this action with an infinite size.") + } + + function Ue(e) { + return null === e || void 0 === e ? Xe() : qe(e) && !c(e) ? e : Xe().withMutations(function(t) { + var n = r(e); + Le(n.size), n.forEach(function(e, n) { + return t.set(n, e) + }) + }) + } + + function qe(e) { + return !(!e || !e[ze]) + } + t(Ue, _e), Ue.of = function() { + var t = e.call(arguments, 0); + return Xe().withMutations(function(e) { + for (var n = 0; n < t.length; n += 2) { + if (n + 1 >= t.length) throw new Error("Missing value for key: " + t[n]); + e.set(t[n], t[n + 1]) + } + }) + }, Ue.prototype.toString = function() { + return this.__toString("Map {", "}") + }, Ue.prototype.get = function(e, t) { + return this._root ? this._root.get(0, void 0, e, t) : t + }, Ue.prototype.set = function(e, t) { + return Qe(this, e, t) + }, Ue.prototype.setIn = function(e, t) { + return this.updateIn(e, y, function() { + return t + }) + }, Ue.prototype.remove = function(e) { + return Qe(this, e, y) + }, Ue.prototype.deleteIn = function(e) { + return this.updateIn(e, function() { + return y + }) + }, Ue.prototype.update = function(e, t, n) { + return 1 === arguments.length ? e(this) : this.updateIn([e], t, n) + }, Ue.prototype.updateIn = function(e, t, n) { + n || (n = t, t = void 0); + var r = function e(t, n, r, o) { + var i = t === y; + var a = n.next(); + if (a.done) { + var u = i ? r : t, + s = o(u); + return s === u ? t : s + } + ge(i || t && t.set, "invalid keyPath"); + var l = a.value; + var c = i ? y : t.get(l, y); + var f = e(c, n, r, o); + return f === c ? t : f === y ? t.remove(l) : (i ? Xe() : t).set(l, f) + }(this, nn(e), t, n); + return r === y ? void 0 : r + }, Ue.prototype.clear = function() { + return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._root = null, this.__hash = void 0, this.__altered = !0, this) : Xe() + }, Ue.prototype.merge = function() { + return rt(this, void 0, arguments) + }, Ue.prototype.mergeWith = function(t) { + return rt(this, t, e.call(arguments, 1)) + }, Ue.prototype.mergeIn = function(t) { + var n = e.call(arguments, 1); + return this.updateIn(t, Xe(), function(e) { + return "function" == typeof e.merge ? e.merge.apply(e, n) : n[n.length - 1] + }) + }, Ue.prototype.mergeDeep = function() { + return rt(this, ot, arguments) + }, Ue.prototype.mergeDeepWith = function(t) { + var n = e.call(arguments, 1); + return rt(this, it(t), n) + }, Ue.prototype.mergeDeepIn = function(t) { + var n = e.call(arguments, 1); + return this.updateIn(t, Xe(), function(e) { + return "function" == typeof e.mergeDeep ? e.mergeDeep.apply(e, n) : n[n.length - 1] + }) + }, Ue.prototype.sort = function(e) { + return Pt(Wt(this, e)) + }, Ue.prototype.sortBy = function(e, t) { + return Pt(Wt(this, t, e)) + }, Ue.prototype.withMutations = function(e) { + var t = this.asMutable(); + return e(t), t.wasAltered() ? t.__ensureOwner(this.__ownerID) : this + }, Ue.prototype.asMutable = function() { + return this.__ownerID ? this : this.__ensureOwner(new x) + }, Ue.prototype.asImmutable = function() { + return this.__ensureOwner() + }, Ue.prototype.wasAltered = function() { + return this.__altered + }, Ue.prototype.__iterator = function(e, t) { + return new Ke(this, e, t) + }, Ue.prototype.__iterate = function(e, t) { + var n = this, + r = 0; + return this._root && this._root.iterate(function(t) { + return r++, e(t[1], t[0], n) + }, t), r + }, Ue.prototype.__ensureOwner = function(e) { + return e === this.__ownerID ? this : e ? Ze(this.size, this._root, e, this.__hash) : (this.__ownerID = e, this.__altered = !1, this) + }, Ue.isMap = qe; + var Fe, ze = "@@__IMMUTABLE_MAP__@@", + Be = Ue.prototype; + + function Ve(e, t) { + this.ownerID = e, this.entries = t + } + + function He(e, t, n) { + this.ownerID = e, this.bitmap = t, this.nodes = n + } + + function We(e, t, n) { + this.ownerID = e, this.count = t, this.nodes = n + } + + function Je(e, t, n) { + this.ownerID = e, this.keyHash = t, this.entries = n + } + + function Ye(e, t, n) { + this.ownerID = e, this.keyHash = t, this.entry = n + } + + function Ke(e, t, n) { + this._type = t, this._reverse = n, this._stack = e._root && $e(e._root) + } + + function Ge(e, t) { + return q(e, t[0], t[1]) + } + + function $e(e, t) { + return { + node: e, + index: 0, + __prev: t + } + } + + function Ze(e, t, n, r) { + var o = Object.create(Be); + return o.size = e, o._root = t, o.__ownerID = n, o.__hash = r, o.__altered = !1, o + } + + function Xe() { + return Fe || (Fe = Ze(0)) + } + + function Qe(e, t, n) { + var r, o; + if (e._root) { + var i = w(b), + a = w(_); + if (r = et(e._root, e.__ownerID, 0, void 0, t, n, i, a), !a.value) return e; + o = e.size + (i.value ? n === y ? -1 : 1 : 0) + } else { + if (n === y) return e; + o = 1, r = new Ve(e.__ownerID, [ + [t, n] + ]) + } + return e.__ownerID ? (e.size = o, e._root = r, e.__hash = void 0, e.__altered = !0, e) : r ? Ze(o, r) : Xe() + } + + function et(e, t, n, r, o, i, a, u) { + return e ? e.update(t, n, r, o, i, a, u) : i === y ? e : (E(u), E(a), new Ye(t, r, [o, i])) + } + + function tt(e) { + return e.constructor === Ye || e.constructor === Je + } + + function nt(e, t, n, r, o) { + if (e.keyHash === r) return new Je(t, r, [e.entry, o]); + var i, a = (0 === n ? e.keyHash : e.keyHash >>> n) & g, + u = (0 === n ? r : r >>> n) & g; + return new He(t, 1 << a | 1 << u, a === u ? [nt(e, t, n + v, r, o)] : (i = new Ye(t, r, o), a < u ? [e, i] : [i, e])) + } + + function rt(e, t, n) { + for (var o = [], i = 0; i < n.length; i++) { + var u = n[i], + s = r(u); + a(u) || (s = s.map(function(e) { + return fe(e) + })), o.push(s) + } + return at(e, t, o) + } + + function ot(e, t, n) { + return e && e.mergeDeep && a(t) ? e.mergeDeep(t) : he(e, t) ? e : t + } + + function it(e) { + return function(t, n, r) { + if (t && t.mergeDeepWith && a(n)) return t.mergeDeepWith(e, n); + var o = e(t, n, r); + return he(t, o) ? t : o + } + } + + function at(e, t, n) { + return 0 === (n = n.filter(function(e) { + return 0 !== e.size + })).length ? e : 0 !== e.size || e.__ownerID || 1 !== n.length ? e.withMutations(function(e) { + for (var r = t ? function(n, r) { + e.update(r, y, function(e) { + return e === y ? n : t(e, n, r) + }) + } : function(t, n) { + e.set(n, t) + }, o = 0; o < n.length; o++) n[o].forEach(r) + }) : e.constructor(n[0]) + } + + function ut(e) { + return e = (e = (858993459 & (e -= e >> 1 & 1431655765)) + (e >> 2 & 858993459)) + (e >> 4) & 252645135, e += e >> 8, 127 & (e += e >> 16) + } + + function st(e, t, n, r) { + var o = r ? e : S(e); + return o[t] = n, o + } + Be[ze] = !0, Be.delete = Be.remove, Be.removeIn = Be.deleteIn, Ve.prototype.get = function(e, t, n, r) { + for (var o = this.entries, i = 0, a = o.length; i < a; i++) + if (he(n, o[i][0])) return o[i][1]; + return r + }, Ve.prototype.update = function(e, t, n, r, o, i, a) { + for (var u = o === y, s = this.entries, l = 0, c = s.length; l < c && !he(r, s[l][0]); l++); + var f = l < c; + if (f ? s[l][1] === o : u) return this; + if (E(a), (u || !f) && E(i), !u || 1 !== s.length) { + if (!f && !u && s.length >= lt) return function(e, t, n, r) { + e || (e = new x); + for (var o = new Ye(e, Ce(n), [n, r]), i = 0; i < t.length; i++) { + var a = t[i]; + o = o.update(e, 0, void 0, a[0], a[1]) + } + return o + }(e, s, r, o); + var p = e && e === this.ownerID, + d = p ? s : S(s); + return f ? u ? l === c - 1 ? d.pop() : d[l] = d.pop() : d[l] = [r, o] : d.push([r, o]), p ? (this.entries = d, this) : new Ve(e, d) + } + }, He.prototype.get = function(e, t, n, r) { + void 0 === t && (t = Ce(n)); + var o = 1 << ((0 === e ? t : t >>> e) & g), + i = this.bitmap; + return 0 == (i & o) ? r : this.nodes[ut(i & o - 1)].get(e + v, t, n, r) + }, He.prototype.update = function(e, t, n, r, o, i, a) { + void 0 === n && (n = Ce(r)); + var u = (0 === t ? n : n >>> t) & g, + s = 1 << u, + l = this.bitmap, + c = 0 != (l & s); + if (!c && o === y) return this; + var f = ut(l & s - 1), + p = this.nodes, + d = c ? p[f] : void 0, + h = et(d, e, t + v, n, r, o, i, a); + if (h === d) return this; + if (!c && h && p.length >= ct) return function(e, t, n, r, o) { + for (var i = 0, a = new Array(m), u = 0; 0 !== n; u++, n >>>= 1) a[u] = 1 & n ? t[i++] : void 0; + return a[r] = o, new We(e, i + 1, a) + }(e, p, l, u, h); + if (c && !h && 2 === p.length && tt(p[1 ^ f])) return p[1 ^ f]; + if (c && h && 1 === p.length && tt(h)) return h; + var b = e && e === this.ownerID, + _ = c ? h ? l : l ^ s : l | s, + w = c ? h ? st(p, f, h, b) : function(e, t, n) { + var r = e.length - 1; + if (n && t === r) return e.pop(), e; + for (var o = new Array(r), i = 0, a = 0; a < r; a++) a === t && (i = 1), o[a] = e[a + i]; + return o + }(p, f, b) : function(e, t, n, r) { + var o = e.length + 1; + if (r && t + 1 === o) return e[t] = n, e; + for (var i = new Array(o), a = 0, u = 0; u < o; u++) u === t ? (i[u] = n, a = -1) : i[u] = e[u + a]; + return i + }(p, f, h, b); + return b ? (this.bitmap = _, this.nodes = w, this) : new He(e, _, w) + }, We.prototype.get = function(e, t, n, r) { + void 0 === t && (t = Ce(n)); + var o = (0 === e ? t : t >>> e) & g, + i = this.nodes[o]; + return i ? i.get(e + v, t, n, r) : r + }, We.prototype.update = function(e, t, n, r, o, i, a) { + void 0 === n && (n = Ce(r)); + var u = (0 === t ? n : n >>> t) & g, + s = o === y, + l = this.nodes, + c = l[u]; + if (s && !c) return this; + var f = et(c, e, t + v, n, r, o, i, a); + if (f === c) return this; + var p = this.count; + if (c) { + if (!f && --p < ft) return function(e, t, n, r) { + for (var o = 0, i = 0, a = new Array(n), u = 0, s = 1, l = t.length; u < l; u++, s <<= 1) { + var c = t[u]; + void 0 !== c && u !== r && (o |= s, a[i++] = c) + } + return new He(e, o, a) + }(e, l, p, u) + } else p++; + var d = e && e === this.ownerID, + h = st(l, u, f, d); + return d ? (this.count = p, this.nodes = h, this) : new We(e, p, h) + }, Je.prototype.get = function(e, t, n, r) { + for (var o = this.entries, i = 0, a = o.length; i < a; i++) + if (he(n, o[i][0])) return o[i][1]; + return r + }, Je.prototype.update = function(e, t, n, r, o, i, a) { + void 0 === n && (n = Ce(r)); + var u = o === y; + if (n !== this.keyHash) return u ? this : (E(a), E(i), nt(this, e, t, n, [r, o])); + for (var s = this.entries, l = 0, c = s.length; l < c && !he(r, s[l][0]); l++); + var f = l < c; + if (f ? s[l][1] === o : u) return this; + if (E(a), (u || !f) && E(i), u && 2 === c) return new Ye(e, this.keyHash, s[1 ^ l]); + var p = e && e === this.ownerID, + d = p ? s : S(s); + return f ? u ? l === c - 1 ? d.pop() : d[l] = d.pop() : d[l] = [r, o] : d.push([r, o]), p ? (this.entries = d, this) : new Je(e, this.keyHash, d) + }, Ye.prototype.get = function(e, t, n, r) { + return he(n, this.entry[0]) ? this.entry[1] : r + }, Ye.prototype.update = function(e, t, n, r, o, i, a) { + var u = o === y, + s = he(r, this.entry[0]); + return (s ? o === this.entry[1] : u) ? this : (E(a), u ? void E(i) : s ? e && e === this.ownerID ? (this.entry[1] = o, this) : new Ye(e, this.keyHash, [r, o]) : (E(i), nt(this, e, t, Ce(r), [r, o]))) + }, Ve.prototype.iterate = Je.prototype.iterate = function(e, t) { + for (var n = this.entries, r = 0, o = n.length - 1; r <= o; r++) + if (!1 === e(n[t ? o - r : r])) return !1 + }, He.prototype.iterate = We.prototype.iterate = function(e, t) { + for (var n = this.nodes, r = 0, o = n.length - 1; r <= o; r++) { + var i = n[t ? o - r : r]; + if (i && !1 === i.iterate(e, t)) return !1 + } + }, Ye.prototype.iterate = function(e, t) { + return e(this.entry) + }, t(Ke, U), Ke.prototype.next = function() { + for (var e = this._type, t = this._stack; t;) { + var n, r = t.node, + o = t.index++; + if (r.entry) { + if (0 === o) return Ge(e, r.entry) + } else if (r.entries) { + if (o <= (n = r.entries.length - 1)) return Ge(e, r.entries[this._reverse ? n - o : o]) + } else if (o <= (n = r.nodes.length - 1)) { + var i = r.nodes[this._reverse ? n - o : o]; + if (i) { + if (i.entry) return Ge(e, i.entry); + t = this._stack = $e(i, t) + } + continue + } + t = this._stack = this._stack.__prev + } + return { + value: void 0, + done: !0 + } + }; + var lt = m / 4, + ct = m / 2, + ft = m / 4; + + function pt(e) { + var t = Et(); + if (null === e || void 0 === e) return t; + if (dt(e)) return e; + var n = o(e), + r = n.size; + return 0 === r ? t : (Le(r), r > 0 && r < m ? wt(0, r, v, null, new mt(n.toArray())) : t.withMutations(function(e) { + e.setSize(r), n.forEach(function(t, n) { + return e.set(n, t) + }) + })) + } + + function dt(e) { + return !(!e || !e[ht]) + } + t(pt, we), pt.of = function() { + return this(arguments) + }, pt.prototype.toString = function() { + return this.__toString("List [", "]") + }, pt.prototype.get = function(e, t) { + if ((e = k(this, e)) >= 0 && e < this.size) { + var n = Ct(this, e += this._origin); + return n && n.array[e & g] + } + return t + }, pt.prototype.set = function(e, t) { + return function(e, t, n) { + if ((t = k(e, t)) != t) return e; + if (t >= e.size || t < 0) return e.withMutations(function(e) { + t < 0 ? kt(e, t).set(0, n) : kt(e, 0, t + 1).set(t, n) + }); + t += e._origin; + var r = e._tail, + o = e._root, + i = w(_); + t >= Ot(e._capacity) ? r = xt(r, e.__ownerID, 0, t, n, i) : o = xt(o, e.__ownerID, e._level, t, n, i); + if (!i.value) return e; + if (e.__ownerID) return e._root = o, e._tail = r, e.__hash = void 0, e.__altered = !0, e; + return wt(e._origin, e._capacity, e._level, o, r) + }(this, e, t) + }, pt.prototype.remove = function(e) { + return this.has(e) ? 0 === e ? this.shift() : e === this.size - 1 ? this.pop() : this.splice(e, 1) : this + }, pt.prototype.insert = function(e, t) { + return this.splice(e, 0, t) + }, pt.prototype.clear = function() { + return 0 === this.size ? this : this.__ownerID ? (this.size = this._origin = this._capacity = 0, this._level = v, this._root = this._tail = null, this.__hash = void 0, this.__altered = !0, this) : Et() + }, pt.prototype.push = function() { + var e = arguments, + t = this.size; + return this.withMutations(function(n) { + kt(n, 0, t + e.length); + for (var r = 0; r < e.length; r++) n.set(t + r, e[r]) + }) + }, pt.prototype.pop = function() { + return kt(this, 0, -1) + }, pt.prototype.unshift = function() { + var e = arguments; + return this.withMutations(function(t) { + kt(t, -e.length); + for (var n = 0; n < e.length; n++) t.set(n, e[n]) + }) + }, pt.prototype.shift = function() { + return kt(this, 1) + }, pt.prototype.merge = function() { + return At(this, void 0, arguments) + }, pt.prototype.mergeWith = function(t) { + return At(this, t, e.call(arguments, 1)) + }, pt.prototype.mergeDeep = function() { + return At(this, ot, arguments) + }, pt.prototype.mergeDeepWith = function(t) { + var n = e.call(arguments, 1); + return At(this, it(t), n) + }, pt.prototype.setSize = function(e) { + return kt(this, 0, e) + }, pt.prototype.slice = function(e, t) { + var n = this.size; + return O(e, t, n) ? this : kt(this, P(e, n), T(t, n)) + }, pt.prototype.__iterator = function(e, t) { + var n = 0, + r = _t(this, t); + return new U(function() { + var t = r(); + return t === bt ? { + value: void 0, + done: !0 + } : q(e, n++, t) + }) + }, pt.prototype.__iterate = function(e, t) { + for (var n, r = 0, o = _t(this, t); + (n = o()) !== bt && !1 !== e(n, r++, this);); + return r + }, pt.prototype.__ensureOwner = function(e) { + return e === this.__ownerID ? this : e ? wt(this._origin, this._capacity, this._level, this._root, this._tail, e, this.__hash) : (this.__ownerID = e, this) + }, pt.isList = dt; + var ht = "@@__IMMUTABLE_LIST__@@", + vt = pt.prototype; + + function mt(e, t) { + this.array = e, this.ownerID = t + } + vt[ht] = !0, vt.delete = vt.remove, vt.setIn = Be.setIn, vt.deleteIn = vt.removeIn = Be.removeIn, vt.update = Be.update, vt.updateIn = Be.updateIn, vt.mergeIn = Be.mergeIn, vt.mergeDeepIn = Be.mergeDeepIn, vt.withMutations = Be.withMutations, vt.asMutable = Be.asMutable, vt.asImmutable = Be.asImmutable, vt.wasAltered = Be.wasAltered, mt.prototype.removeBefore = function(e, t, n) { + if (n === t ? 1 << t : 0 === this.array.length) return this; + var r = n >>> t & g; + if (r >= this.array.length) return new mt([], e); + var o, i = 0 === r; + if (t > 0) { + var a = this.array[r]; + if ((o = a && a.removeBefore(e, t - v, n)) === a && i) return this + } + if (i && !o) return this; + var u = St(this, e); + if (!i) + for (var s = 0; s < r; s++) u.array[s] = void 0; + return o && (u.array[r] = o), u + }, mt.prototype.removeAfter = function(e, t, n) { + if (n === (t ? 1 << t : 0) || 0 === this.array.length) return this; + var r, o = n - 1 >>> t & g; + if (o >= this.array.length) return this; + if (t > 0) { + var i = this.array[o]; + if ((r = i && i.removeAfter(e, t - v, n)) === i && o === this.array.length - 1) return this + } + var a = St(this, e); + return a.array.splice(o + 1), r && (a.array[o] = r), a + }; + var gt, yt, bt = {}; + + function _t(e, t) { + var n = e._origin, + r = e._capacity, + o = Ot(r), + i = e._tail; + return a(e._root, e._level, 0); + + function a(e, u, s) { + return 0 === u ? function(e, a) { + var u = a === o ? i && i.array : e && e.array, + s = a > n ? 0 : n - a, + l = r - a; + l > m && (l = m); + return function() { + if (s === l) return bt; + var e = t ? --l : s++; + return u && u[e] + } + }(e, s) : function(e, o, i) { + var u, s = e && e.array, + l = i > n ? 0 : n - i >> o, + c = 1 + (r - i >> o); + c > m && (c = m); + return function() { + for (;;) { + if (u) { + var e = u(); + if (e !== bt) return e; + u = null + } + if (l === c) return bt; + var n = t ? --c : l++; + u = a(s && s[n], o - v, i + (n << o)) + } + } + }(e, u, s) + } + } + + function wt(e, t, n, r, o, i, a) { + var u = Object.create(vt); + return u.size = t - e, u._origin = e, u._capacity = t, u._level = n, u._root = r, u._tail = o, u.__ownerID = i, u.__hash = a, u.__altered = !1, u + } + + function Et() { + return gt || (gt = wt(0, 0, v)) + } + + function xt(e, t, n, r, o, i) { + var a, u = r >>> n & g, + s = e && u < e.array.length; + if (!s && void 0 === o) return e; + if (n > 0) { + var l = e && e.array[u], + c = xt(l, t, n - v, r, o, i); + return c === l ? e : ((a = St(e, t)).array[u] = c, a) + } + return s && e.array[u] === o ? e : (E(i), a = St(e, t), void 0 === o && u === a.array.length - 1 ? a.array.pop() : a.array[u] = o, a) + } + + function St(e, t) { + return t && e && t === e.ownerID ? e : new mt(e ? e.array.slice() : [], t) + } + + function Ct(e, t) { + if (t >= Ot(e._capacity)) return e._tail; + if (t < 1 << e._level + v) { + for (var n = e._root, r = e._level; n && r > 0;) n = n.array[t >>> r & g], r -= v; + return n + } + } + + function kt(e, t, n) { + void 0 !== t && (t |= 0), void 0 !== n && (n |= 0); + var r = e.__ownerID || new x, + o = e._origin, + i = e._capacity, + a = o + t, + u = void 0 === n ? i : n < 0 ? i + n : o + n; + if (a === o && u === i) return e; + if (a >= u) return e.clear(); + for (var s = e._level, l = e._root, c = 0; a + c < 0;) l = new mt(l && l.array.length ? [void 0, l] : [], r), c += 1 << (s += v); + c && (a += c, o += c, u += c, i += c); + for (var f = Ot(i), p = Ot(u); p >= 1 << s + v;) l = new mt(l && l.array.length ? [l] : [], r), s += v; + var d = e._tail, + h = p < f ? Ct(e, u - 1) : p > f ? new mt([], r) : d; + if (d && p > f && a < i && d.array.length) { + for (var m = l = St(l, r), y = s; y > v; y -= v) { + var b = f >>> y & g; + m = m.array[b] = St(m.array[b], r) + } + m.array[f >>> v & g] = d + } + if (u < i && (h = h && h.removeAfter(r, 0, u)), a >= p) a -= p, u -= p, s = v, l = null, h = h && h.removeBefore(r, 0, a); + else if (a > o || p < f) { + for (c = 0; l;) { + var _ = a >>> s & g; + if (_ !== p >>> s & g) break; + _ && (c += (1 << s) * _), s -= v, l = l.array[_] + } + l && a > o && (l = l.removeBefore(r, s, a - c)), l && p < f && (l = l.removeAfter(r, s, p - c)), c && (a -= c, u -= c) + } + return e.__ownerID ? (e.size = u - a, e._origin = a, e._capacity = u, e._level = s, e._root = l, e._tail = h, e.__hash = void 0, e.__altered = !0, e) : wt(a, u, s, l, h) + } + + function At(e, t, n) { + for (var r = [], i = 0, u = 0; u < n.length; u++) { + var s = n[u], + l = o(s); + l.size > i && (i = l.size), a(s) || (l = l.map(function(e) { + return fe(e) + })), r.push(l) + } + return i > e.size && (e = e.setSize(i)), at(e, t, r) + } + + function Ot(e) { + return e < m ? 0 : e - 1 >>> v << v + } + + function Pt(e) { + return null === e || void 0 === e ? It() : Tt(e) ? e : It().withMutations(function(t) { + var n = r(e); + Le(n.size), n.forEach(function(e, n) { + return t.set(n, e) + }) + }) + } + + function Tt(e) { + return qe(e) && c(e) + } + + function Mt(e, t, n, r) { + var o = Object.create(Pt.prototype); + return o.size = e ? e.size : 0, o._map = e, o._list = t, o.__ownerID = n, o.__hash = r, o + } + + function It() { + return yt || (yt = Mt(Xe(), Et())) + } + + function jt(e, t, n) { + var r, o, i = e._map, + a = e._list, + u = i.get(t), + s = void 0 !== u; + if (n === y) { + if (!s) return e; + a.size >= m && a.size >= 2 * i.size ? (r = (o = a.filter(function(e, t) { + return void 0 !== e && u !== t + })).toKeyedSeq().map(function(e) { + return e[0] + }).flip().toMap(), e.__ownerID && (r.__ownerID = o.__ownerID = e.__ownerID)) : (r = i.remove(t), o = u === a.size - 1 ? a.pop() : a.set(u, void 0)) + } else if (s) { + if (n === a.get(u)[1]) return e; + r = i, o = a.set(u, [t, n]) + } else r = i.set(t, a.size), o = a.set(a.size, [t, n]); + return e.__ownerID ? (e.size = r.size, e._map = r, e._list = o, e.__hash = void 0, e) : Mt(r, o) + } + + function Nt(e, t) { + this._iter = e, this._useKeys = t, this.size = e.size + } + + function Rt(e) { + this._iter = e, this.size = e.size + } + + function Dt(e) { + this._iter = e, this.size = e.size + } + + function Lt(e) { + this._iter = e, this.size = e.size + } + + function Ut(e) { + var t = Qt(e); + return t._iter = e, t.size = e.size, t.flip = function() { + return e + }, t.reverse = function() { + var t = e.reverse.apply(this); + return t.flip = function() { + return e.reverse() + }, t + }, t.has = function(t) { + return e.includes(t) + }, t.includes = function(t) { + return e.has(t) + }, t.cacheResult = en, t.__iterateUncached = function(t, n) { + var r = this; + return e.__iterate(function(e, n) { + return !1 !== t(n, e, r) + }, n) + }, t.__iteratorUncached = function(t, n) { + if (t === N) { + var r = e.__iterator(t, n); + return new U(function() { + var e = r.next(); + if (!e.done) { + var t = e.value[0]; + e.value[0] = e.value[1], e.value[1] = t + } + return e + }) + } + return e.__iterator(t === j ? I : j, n) + }, t + } + + function qt(e, t, n) { + var r = Qt(e); + return r.size = e.size, r.has = function(t) { + return e.has(t) + }, r.get = function(r, o) { + var i = e.get(r, y); + return i === y ? o : t.call(n, i, r, e) + }, r.__iterateUncached = function(r, o) { + var i = this; + return e.__iterate(function(e, o, a) { + return !1 !== r(t.call(n, e, o, a), o, i) + }, o) + }, r.__iteratorUncached = function(r, o) { + var i = e.__iterator(N, o); + return new U(function() { + var o = i.next(); + if (o.done) return o; + var a = o.value, + u = a[0]; + return q(r, u, t.call(n, a[1], u, e), o) + }) + }, r + } + + function Ft(e, t) { + var n = Qt(e); + return n._iter = e, n.size = e.size, n.reverse = function() { + return e + }, e.flip && (n.flip = function() { + var t = Ut(e); + return t.reverse = function() { + return e.flip() + }, t + }), n.get = function(n, r) { + return e.get(t ? n : -1 - n, r) + }, n.has = function(n) { + return e.has(t ? n : -1 - n) + }, n.includes = function(t) { + return e.includes(t) + }, n.cacheResult = en, n.__iterate = function(t, n) { + var r = this; + return e.__iterate(function(e, n) { + return t(e, n, r) + }, !n) + }, n.__iterator = function(t, n) { + return e.__iterator(t, !n) + }, n + } + + function zt(e, t, n, r) { + var o = Qt(e); + return r && (o.has = function(r) { + var o = e.get(r, y); + return o !== y && !!t.call(n, o, r, e) + }, o.get = function(r, o) { + var i = e.get(r, y); + return i !== y && t.call(n, i, r, e) ? i : o + }), o.__iterateUncached = function(o, i) { + var a = this, + u = 0; + return e.__iterate(function(e, i, s) { + if (t.call(n, e, i, s)) return u++, o(e, r ? i : u - 1, a) + }, i), u + }, o.__iteratorUncached = function(o, i) { + var a = e.__iterator(N, i), + u = 0; + return new U(function() { + for (;;) { + var i = a.next(); + if (i.done) return i; + var s = i.value, + l = s[0], + c = s[1]; + if (t.call(n, c, l, e)) return q(o, r ? l : u++, c, i) + } + }) + }, o + } + + function Bt(e, t, n, r) { + var o = e.size; + if (void 0 !== t && (t |= 0), void 0 !== n && (n === 1 / 0 ? n = o : n |= 0), O(t, n, o)) return e; + var i = P(t, o), + a = T(n, o); + if (i != i || a != a) return Bt(e.toSeq().cacheResult(), t, n, r); + var u, s = a - i; + s == s && (u = s < 0 ? 0 : s); + var l = Qt(e); + return l.size = 0 === u ? u : e.size && u || void 0, !r && oe(e) && u >= 0 && (l.get = function(t, n) { + return (t = k(this, t)) >= 0 && t < u ? e.get(t + i, n) : n + }), l.__iterateUncached = function(t, n) { + var o = this; + if (0 === u) return 0; + if (n) return this.cacheResult().__iterate(t, n); + var a = 0, + s = !0, + l = 0; + return e.__iterate(function(e, n) { + if (!s || !(s = a++ < i)) return l++, !1 !== t(e, r ? n : l - 1, o) && l !== u + }), l + }, l.__iteratorUncached = function(t, n) { + if (0 !== u && n) return this.cacheResult().__iterator(t, n); + var o = 0 !== u && e.__iterator(t, n), + a = 0, + s = 0; + return new U(function() { + for (; a++ < i;) o.next(); + if (++s > u) return { + value: void 0, + done: !0 + }; + var e = o.next(); + return r || t === j ? e : q(t, s - 1, t === I ? void 0 : e.value[1], e) + }) + }, l + } + + function Vt(e, t, n, r) { + var o = Qt(e); + return o.__iterateUncached = function(o, i) { + var a = this; + if (i) return this.cacheResult().__iterate(o, i); + var u = !0, + s = 0; + return e.__iterate(function(e, i, l) { + if (!u || !(u = t.call(n, e, i, l))) return s++, o(e, r ? i : s - 1, a) + }), s + }, o.__iteratorUncached = function(o, i) { + var a = this; + if (i) return this.cacheResult().__iterator(o, i); + var u = e.__iterator(N, i), + s = !0, + l = 0; + return new U(function() { + var e, i, c; + do { + if ((e = u.next()).done) return r || o === j ? e : q(o, l++, o === I ? void 0 : e.value[1], e); + var f = e.value; + i = f[0], c = f[1], s && (s = t.call(n, c, i, a)) + } while (s); + return o === N ? e : q(o, i, c, e) + }) + }, o + } + + function Ht(e, t, n) { + var r = Qt(e); + return r.__iterateUncached = function(r, o) { + var i = 0, + u = !1; + return function e(s, l) { + var c = this; + s.__iterate(function(o, s) { + return (!t || l < t) && a(o) ? e(o, l + 1) : !1 === r(o, n ? s : i++, c) && (u = !0), !u + }, o) + }(e, 0), i + }, r.__iteratorUncached = function(r, o) { + var i = e.__iterator(r, o), + u = [], + s = 0; + return new U(function() { + for (; i;) { + var e = i.next(); + if (!1 === e.done) { + var l = e.value; + if (r === N && (l = l[1]), t && !(u.length < t) || !a(l)) return n ? e : q(r, s++, l, e); + u.push(i), i = l.__iterator(r, o) + } else i = u.pop() + } + return { + value: void 0, + done: !0 + } + }) + }, r + } + + function Wt(e, t, n) { + t || (t = tn); + var r = u(e), + o = 0, + i = e.toSeq().map(function(t, r) { + return [r, t, o++, n ? n(t, r, e) : t] + }).toArray(); + return i.sort(function(e, n) { + return t(e[3], n[3]) || e[2] - n[2] + }).forEach(r ? function(e, t) { + i[t].length = 2 + } : function(e, t) { + i[t] = e[1] + }), r ? Y(i) : s(e) ? K(i) : G(i) + } + + function Jt(e, t, n) { + if (t || (t = tn), n) { + var r = e.toSeq().map(function(t, r) { + return [t, n(t, r, e)] + }).reduce(function(e, n) { + return Yt(t, e[1], n[1]) ? n : e + }); + return r && r[0] + } + return e.reduce(function(e, n) { + return Yt(t, e, n) ? n : e + }) + } + + function Yt(e, t, n) { + var r = e(n, t); + return 0 === r && n !== t && (void 0 === n || null === n || n != n) || r > 0 + } + + function Kt(e, t, r) { + var o = Qt(e); + return o.size = new ee(r).map(function(e) { + return e.size + }).min(), o.__iterate = function(e, t) { + for (var n, r = this.__iterator(j, t), o = 0; !(n = r.next()).done && !1 !== e(n.value, o++, this);); + return o + }, o.__iteratorUncached = function(e, o) { + var i = r.map(function(e) { + return e = n(e), V(o ? e.reverse() : e) + }), + a = 0, + u = !1; + return new U(function() { + var n; + return u || (n = i.map(function(e) { + return e.next() + }), u = n.some(function(e) { + return e.done + })), u ? { + value: void 0, + done: !0 + } : q(e, a++, t.apply(null, n.map(function(e) { + return e.value + }))) + }) + }, o + } + + function Gt(e, t) { + return oe(e) ? t : e.constructor(t) + } + + function $t(e) { + if (e !== Object(e)) throw new TypeError("Expected [K, V] tuple: " + e) + } + + function Zt(e) { + return Le(e.size), C(e) + } + + function Xt(e) { + return u(e) ? r : s(e) ? o : i + } + + function Qt(e) { + return Object.create((u(e) ? Y : s(e) ? K : G).prototype) + } + + function en() { + return this._iter.cacheResult ? (this._iter.cacheResult(), this.size = this._iter.size, this) : J.prototype.cacheResult.call(this) + } + + function tn(e, t) { + return e > t ? 1 : e < t ? -1 : 0 + } + + function nn(e) { + var t = V(e); + if (!t) { + if (!W(e)) throw new TypeError("Expected iterable or array-like: " + e); + t = V(n(e)) + } + return t + } + + function rn(e, t) { + var n, r = function(i) { + if (i instanceof r) return i; + if (!(this instanceof r)) return new r(i); + if (!n) { + n = !0; + var a = Object.keys(e); + ! function(e, t) { + try { + t.forEach(function(e, t) { + Object.defineProperty(e, t, { + get: function() { + return this.get(t) + }, + set: function(e) { + ge(this.__ownerID, "Cannot set on an immutable record."), this.set(t, e) + } + }) + }.bind(void 0, e)) + } catch (e) {} + }(o, a), o.size = a.length, o._name = t, o._keys = a, o._defaultValues = e + } + this._map = Ue(i) + }, + o = r.prototype = Object.create(on); + return o.constructor = r, r + } + t(Pt, Ue), Pt.of = function() { + return this(arguments) + }, Pt.prototype.toString = function() { + return this.__toString("OrderedMap {", "}") + }, Pt.prototype.get = function(e, t) { + var n = this._map.get(e); + return void 0 !== n ? this._list.get(n)[1] : t + }, Pt.prototype.clear = function() { + return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._map.clear(), this._list.clear(), this) : It() + }, Pt.prototype.set = function(e, t) { + return jt(this, e, t) + }, Pt.prototype.remove = function(e) { + return jt(this, e, y) + }, Pt.prototype.wasAltered = function() { + return this._map.wasAltered() || this._list.wasAltered() + }, Pt.prototype.__iterate = function(e, t) { + var n = this; + return this._list.__iterate(function(t) { + return t && e(t[1], t[0], n) + }, t) + }, Pt.prototype.__iterator = function(e, t) { + return this._list.fromEntrySeq().__iterator(e, t) + }, Pt.prototype.__ensureOwner = function(e) { + if (e === this.__ownerID) return this; + var t = this._map.__ensureOwner(e), + n = this._list.__ensureOwner(e); + return e ? Mt(t, n, e, this.__hash) : (this.__ownerID = e, this._map = t, this._list = n, this) + }, Pt.isOrderedMap = Tt, Pt.prototype[h] = !0, Pt.prototype.delete = Pt.prototype.remove, t(Nt, Y), Nt.prototype.get = function(e, t) { + return this._iter.get(e, t) + }, Nt.prototype.has = function(e) { + return this._iter.has(e) + }, Nt.prototype.valueSeq = function() { + return this._iter.valueSeq() + }, Nt.prototype.reverse = function() { + var e = this, + t = Ft(this, !0); + return this._useKeys || (t.valueSeq = function() { + return e._iter.toSeq().reverse() + }), t + }, Nt.prototype.map = function(e, t) { + var n = this, + r = qt(this, e, t); + return this._useKeys || (r.valueSeq = function() { + return n._iter.toSeq().map(e, t) + }), r + }, Nt.prototype.__iterate = function(e, t) { + var n, r = this; + return this._iter.__iterate(this._useKeys ? function(t, n) { + return e(t, n, r) + } : (n = t ? Zt(this) : 0, function(o) { + return e(o, t ? --n : n++, r) + }), t) + }, Nt.prototype.__iterator = function(e, t) { + if (this._useKeys) return this._iter.__iterator(e, t); + var n = this._iter.__iterator(j, t), + r = t ? Zt(this) : 0; + return new U(function() { + var o = n.next(); + return o.done ? o : q(e, t ? --r : r++, o.value, o) + }) + }, Nt.prototype[h] = !0, t(Rt, K), Rt.prototype.includes = function(e) { + return this._iter.includes(e) + }, Rt.prototype.__iterate = function(e, t) { + var n = this, + r = 0; + return this._iter.__iterate(function(t) { + return e(t, r++, n) + }, t) + }, Rt.prototype.__iterator = function(e, t) { + var n = this._iter.__iterator(j, t), + r = 0; + return new U(function() { + var t = n.next(); + return t.done ? t : q(e, r++, t.value, t) + }) + }, t(Dt, G), Dt.prototype.has = function(e) { + return this._iter.includes(e) + }, Dt.prototype.__iterate = function(e, t) { + var n = this; + return this._iter.__iterate(function(t) { + return e(t, t, n) + }, t) + }, Dt.prototype.__iterator = function(e, t) { + var n = this._iter.__iterator(j, t); + return new U(function() { + var t = n.next(); + return t.done ? t : q(e, t.value, t.value, t) + }) + }, t(Lt, Y), Lt.prototype.entrySeq = function() { + return this._iter.toSeq() + }, Lt.prototype.__iterate = function(e, t) { + var n = this; + return this._iter.__iterate(function(t) { + if (t) { + $t(t); + var r = a(t); + return e(r ? t.get(1) : t[1], r ? t.get(0) : t[0], n) + } + }, t) + }, Lt.prototype.__iterator = function(e, t) { + var n = this._iter.__iterator(j, t); + return new U(function() { + for (;;) { + var t = n.next(); + if (t.done) return t; + var r = t.value; + if (r) { + $t(r); + var o = a(r); + return q(e, o ? r.get(0) : r[0], o ? r.get(1) : r[1], t) + } + } + }) + }, Rt.prototype.cacheResult = Nt.prototype.cacheResult = Dt.prototype.cacheResult = Lt.prototype.cacheResult = en, t(rn, _e), rn.prototype.toString = function() { + return this.__toString(un(this) + " {", "}") + }, rn.prototype.has = function(e) { + return this._defaultValues.hasOwnProperty(e) + }, rn.prototype.get = function(e, t) { + if (!this.has(e)) return t; + var n = this._defaultValues[e]; + return this._map ? this._map.get(e, n) : n + }, rn.prototype.clear = function() { + if (this.__ownerID) return this._map && this._map.clear(), this; + var e = this.constructor; + return e._empty || (e._empty = an(this, Xe())) + }, rn.prototype.set = function(e, t) { + if (!this.has(e)) throw new Error('Cannot set unknown key "' + e + '" on ' + un(this)); + if (this._map && !this._map.has(e) && t === this._defaultValues[e]) return this; + var n = this._map && this._map.set(e, t); + return this.__ownerID || n === this._map ? this : an(this, n) + }, rn.prototype.remove = function(e) { + if (!this.has(e)) return this; + var t = this._map && this._map.remove(e); + return this.__ownerID || t === this._map ? this : an(this, t) + }, rn.prototype.wasAltered = function() { + return this._map.wasAltered() + }, rn.prototype.__iterator = function(e, t) { + var n = this; + return r(this._defaultValues).map(function(e, t) { + return n.get(t) + }).__iterator(e, t) + }, rn.prototype.__iterate = function(e, t) { + var n = this; + return r(this._defaultValues).map(function(e, t) { + return n.get(t) + }).__iterate(e, t) + }, rn.prototype.__ensureOwner = function(e) { + if (e === this.__ownerID) return this; + var t = this._map && this._map.__ensureOwner(e); + return e ? an(this, t, e) : (this.__ownerID = e, this._map = t, this) + }; + var on = rn.prototype; + + function an(e, t, n) { + var r = Object.create(Object.getPrototypeOf(e)); + return r._map = t, r.__ownerID = n, r + } + + function un(e) { + return e._name || e.constructor.name || "Record" + } + + function sn(e) { + return null === e || void 0 === e ? vn() : ln(e) && !c(e) ? e : vn().withMutations(function(t) { + var n = i(e); + Le(n.size), n.forEach(function(e) { + return t.add(e) + }) + }) + } + + function ln(e) { + return !(!e || !e[fn]) + } + on.delete = on.remove, on.deleteIn = on.removeIn = Be.removeIn, on.merge = Be.merge, on.mergeWith = Be.mergeWith, on.mergeIn = Be.mergeIn, on.mergeDeep = Be.mergeDeep, on.mergeDeepWith = Be.mergeDeepWith, on.mergeDeepIn = Be.mergeDeepIn, on.setIn = Be.setIn, on.update = Be.update, on.updateIn = Be.updateIn, on.withMutations = Be.withMutations, on.asMutable = Be.asMutable, on.asImmutable = Be.asImmutable, t(sn, Ee), sn.of = function() { + return this(arguments) + }, sn.fromKeys = function(e) { + return this(r(e).keySeq()) + }, sn.prototype.toString = function() { + return this.__toString("Set {", "}") + }, sn.prototype.has = function(e) { + return this._map.has(e) + }, sn.prototype.add = function(e) { + return dn(this, this._map.set(e, !0)) + }, sn.prototype.remove = function(e) { + return dn(this, this._map.remove(e)) + }, sn.prototype.clear = function() { + return dn(this, this._map.clear()) + }, sn.prototype.union = function() { + var t = e.call(arguments, 0); + return 0 === (t = t.filter(function(e) { + return 0 !== e.size + })).length ? this : 0 !== this.size || this.__ownerID || 1 !== t.length ? this.withMutations(function(e) { + for (var n = 0; n < t.length; n++) i(t[n]).forEach(function(t) { + return e.add(t) + }) + }) : this.constructor(t[0]) + }, sn.prototype.intersect = function() { + var t = e.call(arguments, 0); + if (0 === t.length) return this; + t = t.map(function(e) { + return i(e) + }); + var n = this; + return this.withMutations(function(e) { + n.forEach(function(n) { + t.every(function(e) { + return e.includes(n) + }) || e.remove(n) + }) + }) + }, sn.prototype.subtract = function() { + var t = e.call(arguments, 0); + if (0 === t.length) return this; + t = t.map(function(e) { + return i(e) + }); + var n = this; + return this.withMutations(function(e) { + n.forEach(function(n) { + t.some(function(e) { + return e.includes(n) + }) && e.remove(n) + }) + }) + }, sn.prototype.merge = function() { + return this.union.apply(this, arguments) + }, sn.prototype.mergeWith = function(t) { + var n = e.call(arguments, 1); + return this.union.apply(this, n) + }, sn.prototype.sort = function(e) { + return mn(Wt(this, e)) + }, sn.prototype.sortBy = function(e, t) { + return mn(Wt(this, t, e)) + }, sn.prototype.wasAltered = function() { + return this._map.wasAltered() + }, sn.prototype.__iterate = function(e, t) { + var n = this; + return this._map.__iterate(function(t, r) { + return e(r, r, n) + }, t) + }, sn.prototype.__iterator = function(e, t) { + return this._map.map(function(e, t) { + return t + }).__iterator(e, t) + }, sn.prototype.__ensureOwner = function(e) { + if (e === this.__ownerID) return this; + var t = this._map.__ensureOwner(e); + return e ? this.__make(t, e) : (this.__ownerID = e, this._map = t, this) + }, sn.isSet = ln; + var cn, fn = "@@__IMMUTABLE_SET__@@", + pn = sn.prototype; + + function dn(e, t) { + return e.__ownerID ? (e.size = t.size, e._map = t, e) : t === e._map ? e : 0 === t.size ? e.__empty() : e.__make(t) + } + + function hn(e, t) { + var n = Object.create(pn); + return n.size = e ? e.size : 0, n._map = e, n.__ownerID = t, n + } + + function vn() { + return cn || (cn = hn(Xe())) + } + + function mn(e) { + return null === e || void 0 === e ? wn() : gn(e) ? e : wn().withMutations(function(t) { + var n = i(e); + Le(n.size), n.forEach(function(e) { + return t.add(e) + }) + }) + } + + function gn(e) { + return ln(e) && c(e) + } + pn[fn] = !0, pn.delete = pn.remove, pn.mergeDeep = pn.merge, pn.mergeDeepWith = pn.mergeWith, pn.withMutations = Be.withMutations, pn.asMutable = Be.asMutable, pn.asImmutable = Be.asImmutable, pn.__empty = vn, pn.__make = hn, t(mn, sn), mn.of = function() { + return this(arguments) + }, mn.fromKeys = function(e) { + return this(r(e).keySeq()) + }, mn.prototype.toString = function() { + return this.__toString("OrderedSet {", "}") + }, mn.isOrderedSet = gn; + var yn, bn = mn.prototype; + + function _n(e, t) { + var n = Object.create(bn); + return n.size = e ? e.size : 0, n._map = e, n.__ownerID = t, n + } + + function wn() { + return yn || (yn = _n(It())) + } + + function En(e) { + return null === e || void 0 === e ? On() : xn(e) ? e : On().unshiftAll(e) + } + + function xn(e) { + return !(!e || !e[Cn]) + } + bn[h] = !0, bn.__empty = wn, bn.__make = _n, t(En, we), En.of = function() { + return this(arguments) + }, En.prototype.toString = function() { + return this.__toString("Stack [", "]") + }, En.prototype.get = function(e, t) { + var n = this._head; + for (e = k(this, e); n && e--;) n = n.next; + return n ? n.value : t + }, En.prototype.peek = function() { + return this._head && this._head.value + }, En.prototype.push = function() { + if (0 === arguments.length) return this; + for (var e = this.size + arguments.length, t = this._head, n = arguments.length - 1; n >= 0; n--) t = { + value: arguments[n], + next: t + }; + return this.__ownerID ? (this.size = e, this._head = t, this.__hash = void 0, this.__altered = !0, this) : An(e, t) + }, En.prototype.pushAll = function(e) { + if (0 === (e = o(e)).size) return this; + Le(e.size); + var t = this.size, + n = this._head; + return e.reverse().forEach(function(e) { + t++, n = { + value: e, + next: n + } + }), this.__ownerID ? (this.size = t, this._head = n, this.__hash = void 0, this.__altered = !0, this) : An(t, n) + }, En.prototype.pop = function() { + return this.slice(1) + }, En.prototype.unshift = function() { + return this.push.apply(this, arguments) + }, En.prototype.unshiftAll = function(e) { + return this.pushAll(e) + }, En.prototype.shift = function() { + return this.pop.apply(this, arguments) + }, En.prototype.clear = function() { + return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._head = void 0, this.__hash = void 0, this.__altered = !0, this) : On() + }, En.prototype.slice = function(e, t) { + if (O(e, t, this.size)) return this; + var n = P(e, this.size); + if (T(t, this.size) !== this.size) return we.prototype.slice.call(this, e, t); + for (var r = this.size - n, o = this._head; n--;) o = o.next; + return this.__ownerID ? (this.size = r, this._head = o, this.__hash = void 0, this.__altered = !0, this) : An(r, o) + }, En.prototype.__ensureOwner = function(e) { + return e === this.__ownerID ? this : e ? An(this.size, this._head, e, this.__hash) : (this.__ownerID = e, this.__altered = !1, this) + }, En.prototype.__iterate = function(e, t) { + if (t) return this.reverse().__iterate(e); + for (var n = 0, r = this._head; r && !1 !== e(r.value, n++, this);) r = r.next; + return n + }, En.prototype.__iterator = function(e, t) { + if (t) return this.reverse().__iterator(e); + var n = 0, + r = this._head; + return new U(function() { + if (r) { + var t = r.value; + return r = r.next, q(e, n++, t) + } + return { + value: void 0, + done: !0 + } + }) + }, En.isStack = xn; + var Sn, Cn = "@@__IMMUTABLE_STACK__@@", + kn = En.prototype; + + function An(e, t, n, r) { + var o = Object.create(kn); + return o.size = e, o._head = t, o.__ownerID = n, o.__hash = r, o.__altered = !1, o + } + + function On() { + return Sn || (Sn = An(0)) + } + + function Pn(e, t) { + var n = function(n) { + e.prototype[n] = t[n] + }; + return Object.keys(t).forEach(n), Object.getOwnPropertySymbols && Object.getOwnPropertySymbols(t).forEach(n), e + } + kn[Cn] = !0, kn.withMutations = Be.withMutations, kn.asMutable = Be.asMutable, kn.asImmutable = Be.asImmutable, kn.wasAltered = Be.wasAltered, n.Iterator = U, Pn(n, { + toArray: function() { + Le(this.size); + var e = new Array(this.size || 0); + return this.valueSeq().__iterate(function(t, n) { + e[n] = t + }), e + }, + toIndexedSeq: function() { + return new Rt(this) + }, + toJS: function() { + return this.toSeq().map(function(e) { + return e && "function" == typeof e.toJS ? e.toJS() : e + }).__toJS() + }, + toJSON: function() { + return this.toSeq().map(function(e) { + return e && "function" == typeof e.toJSON ? e.toJSON() : e + }).__toJS() + }, + toKeyedSeq: function() { + return new Nt(this, !0) + }, + toMap: function() { + return Ue(this.toKeyedSeq()) + }, + toObject: function() { + Le(this.size); + var e = {}; + return this.__iterate(function(t, n) { + e[n] = t + }), e + }, + toOrderedMap: function() { + return Pt(this.toKeyedSeq()) + }, + toOrderedSet: function() { + return mn(u(this) ? this.valueSeq() : this) + }, + toSet: function() { + return sn(u(this) ? this.valueSeq() : this) + }, + toSetSeq: function() { + return new Dt(this) + }, + toSeq: function() { + return s(this) ? this.toIndexedSeq() : u(this) ? this.toKeyedSeq() : this.toSetSeq() + }, + toStack: function() { + return En(u(this) ? this.valueSeq() : this) + }, + toList: function() { + return pt(u(this) ? this.valueSeq() : this) + }, + toString: function() { + return "[Iterable]" + }, + __toString: function(e, t) { + return 0 === this.size ? e + t : e + " " + this.toSeq().map(this.__toStringMapper).join(", ") + " " + t + }, + concat: function() { + return Gt(this, function(e, t) { + var n = u(e), + o = [e].concat(t).map(function(e) { + return a(e) ? n && (e = r(e)) : e = n ? ae(e) : ue(Array.isArray(e) ? e : [e]), e + }).filter(function(e) { + return 0 !== e.size + }); + if (0 === o.length) return e; + if (1 === o.length) { + var i = o[0]; + if (i === e || n && u(i) || s(e) && s(i)) return i + } + var l = new ee(o); + return n ? l = l.toKeyedSeq() : s(e) || (l = l.toSetSeq()), (l = l.flatten(!0)).size = o.reduce(function(e, t) { + if (void 0 !== e) { + var n = t.size; + if (void 0 !== n) return e + n + } + }, 0), l + }(this, e.call(arguments, 0))) + }, + includes: function(e) { + return this.some(function(t) { + return he(t, e) + }) + }, + entries: function() { + return this.__iterator(N) + }, + every: function(e, t) { + Le(this.size); + var n = !0; + return this.__iterate(function(r, o, i) { + if (!e.call(t, r, o, i)) return n = !1, !1 + }), n + }, + filter: function(e, t) { + return Gt(this, zt(this, e, t, !0)) + }, + find: function(e, t, n) { + var r = this.findEntry(e, t); + return r ? r[1] : n + }, + forEach: function(e, t) { + return Le(this.size), this.__iterate(t ? e.bind(t) : e) + }, + join: function(e) { + Le(this.size), e = void 0 !== e ? "" + e : ","; + var t = "", + n = !0; + return this.__iterate(function(r) { + n ? n = !1 : t += e, t += null !== r && void 0 !== r ? r.toString() : "" + }), t + }, + keys: function() { + return this.__iterator(I) + }, + map: function(e, t) { + return Gt(this, qt(this, e, t)) + }, + reduce: function(e, t, n) { + var r, o; + return Le(this.size), arguments.length < 2 ? o = !0 : r = t, this.__iterate(function(t, i, a) { + o ? (o = !1, r = t) : r = e.call(n, r, t, i, a) + }), r + }, + reduceRight: function(e, t, n) { + var r = this.toKeyedSeq().reverse(); + return r.reduce.apply(r, arguments) + }, + reverse: function() { + return Gt(this, Ft(this, !0)) + }, + slice: function(e, t) { + return Gt(this, Bt(this, e, t, !0)) + }, + some: function(e, t) { + return !this.every(Nn(e), t) + }, + sort: function(e) { + return Gt(this, Wt(this, e)) + }, + values: function() { + return this.__iterator(j) + }, + butLast: function() { + return this.slice(0, -1) + }, + isEmpty: function() { + return void 0 !== this.size ? 0 === this.size : !this.some(function() { + return !0 + }) + }, + count: function(e, t) { + return C(e ? this.toSeq().filter(e, t) : this) + }, + countBy: function(e, t) { + return function(e, t, n) { + var r = Ue().asMutable(); + return e.__iterate(function(o, i) { + r.update(t.call(n, o, i, e), 0, function(e) { + return e + 1 + }) + }), r.asImmutable() + }(this, e, t) + }, + equals: function(e) { + return ve(this, e) + }, + entrySeq: function() { + var e = this; + if (e._cache) return new ee(e._cache); + var t = e.toSeq().map(jn).toIndexedSeq(); + return t.fromEntrySeq = function() { + return e.toSeq() + }, t + }, + filterNot: function(e, t) { + return this.filter(Nn(e), t) + }, + findEntry: function(e, t, n) { + var r = n; + return this.__iterate(function(n, o, i) { + if (e.call(t, n, o, i)) return r = [o, n], !1 + }), r + }, + findKey: function(e, t) { + var n = this.findEntry(e, t); + return n && n[0] + }, + findLast: function(e, t, n) { + return this.toKeyedSeq().reverse().find(e, t, n) + }, + findLastEntry: function(e, t, n) { + return this.toKeyedSeq().reverse().findEntry(e, t, n) + }, + findLastKey: function(e, t) { + return this.toKeyedSeq().reverse().findKey(e, t) + }, + first: function() { + return this.find(A) + }, + flatMap: function(e, t) { + return Gt(this, function(e, t, n) { + var r = Xt(e); + return e.toSeq().map(function(o, i) { + return r(t.call(n, o, i, e)) + }).flatten(!0) + }(this, e, t)) + }, + flatten: function(e) { + return Gt(this, Ht(this, e, !0)) + }, + fromEntrySeq: function() { + return new Lt(this) + }, + get: function(e, t) { + return this.find(function(t, n) { + return he(n, e) + }, void 0, t) + }, + getIn: function(e, t) { + for (var n, r = this, o = nn(e); !(n = o.next()).done;) { + var i = n.value; + if ((r = r && r.get ? r.get(i, y) : y) === y) return t + } + return r + }, + groupBy: function(e, t) { + return function(e, t, n) { + var r = u(e), + o = (c(e) ? Pt() : Ue()).asMutable(); + e.__iterate(function(i, a) { + o.update(t.call(n, i, a, e), function(e) { + return (e = e || []).push(r ? [a, i] : i), e + }) + }); + var i = Xt(e); + return o.map(function(t) { + return Gt(e, i(t)) + }) + }(this, e, t) + }, + has: function(e) { + return this.get(e, y) !== y + }, + hasIn: function(e) { + return this.getIn(e, y) !== y + }, + isSubset: function(e) { + return e = "function" == typeof e.includes ? e : n(e), this.every(function(t) { + return e.includes(t) + }) + }, + isSuperset: function(e) { + return (e = "function" == typeof e.isSubset ? e : n(e)).isSubset(this) + }, + keyOf: function(e) { + return this.findKey(function(t) { + return he(t, e) + }) + }, + keySeq: function() { + return this.toSeq().map(In).toIndexedSeq() + }, + last: function() { + return this.toSeq().reverse().first() + }, + lastKeyOf: function(e) { + return this.toKeyedSeq().reverse().keyOf(e) + }, + max: function(e) { + return Jt(this, e) + }, + maxBy: function(e, t) { + return Jt(this, t, e) + }, + min: function(e) { + return Jt(this, e ? Rn(e) : Un) + }, + minBy: function(e, t) { + return Jt(this, t ? Rn(t) : Un, e) + }, + rest: function() { + return this.slice(1) + }, + skip: function(e) { + return this.slice(Math.max(0, e)) + }, + skipLast: function(e) { + return Gt(this, this.toSeq().reverse().skip(e).reverse()) + }, + skipWhile: function(e, t) { + return Gt(this, Vt(this, e, t, !0)) + }, + skipUntil: function(e, t) { + return this.skipWhile(Nn(e), t) + }, + sortBy: function(e, t) { + return Gt(this, Wt(this, t, e)) + }, + take: function(e) { + return this.slice(0, Math.max(0, e)) + }, + takeLast: function(e) { + return Gt(this, this.toSeq().reverse().take(e).reverse()) + }, + takeWhile: function(e, t) { + return Gt(this, function(e, t, n) { + var r = Qt(e); + return r.__iterateUncached = function(r, o) { + var i = this; + if (o) return this.cacheResult().__iterate(r, o); + var a = 0; + return e.__iterate(function(e, o, u) { + return t.call(n, e, o, u) && ++a && r(e, o, i) + }), a + }, r.__iteratorUncached = function(r, o) { + var i = this; + if (o) return this.cacheResult().__iterator(r, o); + var a = e.__iterator(N, o), + u = !0; + return new U(function() { + if (!u) return { + value: void 0, + done: !0 + }; + var e = a.next(); + if (e.done) return e; + var o = e.value, + s = o[0], + l = o[1]; + return t.call(n, l, s, i) ? r === N ? e : q(r, s, l, e) : (u = !1, { + value: void 0, + done: !0 + }) + }) + }, r + }(this, e, t)) + }, + takeUntil: function(e, t) { + return this.takeWhile(Nn(e), t) + }, + valueSeq: function() { + return this.toIndexedSeq() + }, + hashCode: function() { + return this.__hash || (this.__hash = function(e) { + if (e.size === 1 / 0) return 0; + var t = c(e), + n = u(e), + r = t ? 1 : 0; + return function(e, t) { + return t = xe(t, 3432918353), t = xe(t << 15 | t >>> -15, 461845907), t = xe(t << 13 | t >>> -13, 5), t = xe((t = (t + 3864292196 | 0) ^ e) ^ t >>> 16, 2246822507), t = Se((t = xe(t ^ t >>> 13, 3266489909)) ^ t >>> 16) + }(e.__iterate(n ? t ? function(e, t) { + r = 31 * r + qn(Ce(e), Ce(t)) | 0 + } : function(e, t) { + r = r + qn(Ce(e), Ce(t)) | 0 + } : t ? function(e) { + r = 31 * r + Ce(e) | 0 + } : function(e) { + r = r + Ce(e) | 0 + }), r) + }(this)) + } + }); + var Tn = n.prototype; + Tn[f] = !0, Tn[L] = Tn.values, Tn.__toJS = Tn.toArray, Tn.__toStringMapper = Dn, Tn.inspect = Tn.toSource = function() { + return this.toString() + }, Tn.chain = Tn.flatMap, Tn.contains = Tn.includes, Pn(r, { + flip: function() { + return Gt(this, Ut(this)) + }, + mapEntries: function(e, t) { + var n = this, + r = 0; + return Gt(this, this.toSeq().map(function(o, i) { + return e.call(t, [i, o], r++, n) + }).fromEntrySeq()) + }, + mapKeys: function(e, t) { + var n = this; + return Gt(this, this.toSeq().flip().map(function(r, o) { + return e.call(t, r, o, n) + }).flip()) + } + }); + var Mn = r.prototype; + + function In(e, t) { + return t + } + + function jn(e, t) { + return [t, e] + } + + function Nn(e) { + return function() { + return !e.apply(this, arguments) + } + } + + function Rn(e) { + return function() { + return -e.apply(this, arguments) + } + } + + function Dn(e) { + return "string" == typeof e ? JSON.stringify(e) : String(e) + } + + function Ln() { + return S(arguments) + } + + function Un(e, t) { + return e < t ? 1 : e > t ? -1 : 0 + } + + function qn(e, t) { + return e ^ t + 2654435769 + (e << 6) + (e >> 2) | 0 + } + return Mn[p] = !0, Mn[L] = Tn.entries, Mn.__toJS = Tn.toObject, Mn.__toStringMapper = function(e, t) { + return JSON.stringify(t) + ": " + Dn(e) + }, Pn(o, { + toKeyedSeq: function() { + return new Nt(this, !1) + }, + filter: function(e, t) { + return Gt(this, zt(this, e, t, !1)) + }, + findIndex: function(e, t) { + var n = this.findEntry(e, t); + return n ? n[0] : -1 + }, + indexOf: function(e) { + var t = this.keyOf(e); + return void 0 === t ? -1 : t + }, + lastIndexOf: function(e) { + var t = this.lastKeyOf(e); + return void 0 === t ? -1 : t + }, + reverse: function() { + return Gt(this, Ft(this, !1)) + }, + slice: function(e, t) { + return Gt(this, Bt(this, e, t, !1)) + }, + splice: function(e, t) { + var n = arguments.length; + if (t = Math.max(0 | t, 0), 0 === n || 2 === n && !t) return this; + e = P(e, e < 0 ? this.count() : this.size); + var r = this.slice(0, e); + return Gt(this, 1 === n ? r : r.concat(S(arguments, 2), this.slice(e + t))) + }, + findLastIndex: function(e, t) { + var n = this.findLastEntry(e, t); + return n ? n[0] : -1 + }, + first: function() { + return this.get(0) + }, + flatten: function(e) { + return Gt(this, Ht(this, e, !1)) + }, + get: function(e, t) { + return (e = k(this, e)) < 0 || this.size === 1 / 0 || void 0 !== this.size && e > this.size ? t : this.find(function(t, n) { + return n === e + }, void 0, t) + }, + has: function(e) { + return (e = k(this, e)) >= 0 && (void 0 !== this.size ? this.size === 1 / 0 || e < this.size : -1 !== this.indexOf(e)) + }, + interpose: function(e) { + return Gt(this, function(e, t) { + var n = Qt(e); + return n.size = e.size && 2 * e.size - 1, n.__iterateUncached = function(n, r) { + var o = this, + i = 0; + return e.__iterate(function(e, r) { + return (!i || !1 !== n(t, i++, o)) && !1 !== n(e, i++, o) + }, r), i + }, n.__iteratorUncached = function(n, r) { + var o, i = e.__iterator(j, r), + a = 0; + return new U(function() { + return (!o || a % 2) && (o = i.next()).done ? o : a % 2 ? q(n, a++, t) : q(n, a++, o.value, o) + }) + }, n + }(this, e)) + }, + interleave: function() { + var e = [this].concat(S(arguments)), + t = Kt(this.toSeq(), K.of, e), + n = t.flatten(!0); + return t.size && (n.size = t.size * e.length), Gt(this, n) + }, + keySeq: function() { + return ye(0, this.size) + }, + last: function() { + return this.get(-1) + }, + skipWhile: function(e, t) { + return Gt(this, Vt(this, e, t, !1)) + }, + zip: function() { + return Gt(this, Kt(this, Ln, [this].concat(S(arguments)))) + }, + zipWith: function(e) { + var t = S(arguments); + return t[0] = this, Gt(this, Kt(this, e, t)) + } + }), o.prototype[d] = !0, o.prototype[h] = !0, Pn(i, { + get: function(e, t) { + return this.has(e) ? e : t + }, + includes: function(e) { + return this.has(e) + }, + keySeq: function() { + return this.valueSeq() + } + }), i.prototype.has = Tn.includes, i.prototype.contains = i.prototype.includes, Pn(Y, r.prototype), Pn(K, o.prototype), Pn(G, i.prototype), Pn(_e, r.prototype), Pn(we, o.prototype), Pn(Ee, i.prototype), { + Iterable: n, + Seq: J, + Collection: be, + Map: Ue, + OrderedMap: Pt, + List: pt, + Stack: En, + Set: sn, + OrderedSet: mn, + Record: rn, + Range: ye, + Repeat: me, + is: he, + fromJS: fe + } + }, e.exports = r() + }, function(e, t, n) { + "use strict"; + var r = function(e) {}; + e.exports = function(e, t, n, o, i, a, u, s) { + if (r(t), !e) { + var l; + if (void 0 === t) l = new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings."); + else { + var c = [n, o, i, a, u, s], + f = 0; + (l = new Error(t.replace(/%s/g, function() { + return c[f++] + }))).name = "Invariant Violation" + } + throw l.framesToPop = 1, l + } + } + }, function(e, t, n) { + "use strict"; + var r = n(34); + e.exports = r + }, function(e, t, n) { + "use strict"; + (function(e) { + Object.defineProperty(t, "__esModule", { + value: !0 + }), t.getCommonExtensions = t.getExtensions = t.escapeDeepLinkPath = t.createDeepLinkPath = t.shallowEqualKeys = t.buildFormData = t.sorters = t.btoa = t.serializeSearch = t.parseSearch = t.getSampleSchema = t.validateParam = t.validatePattern = t.validateMinLength = t.validateMaxLength = t.validateGuid = t.validateDateTime = t.validateString = t.validateBoolean = t.validateFile = t.validateInteger = t.validateNumber = t.validateMinimum = t.validateMaximum = t.propChecker = t.memoize = t.isImmutable = void 0; + var r = _(n(41)), + o = _(n(18)), + i = _(n(91)), + a = _(n(23)), + u = _(n(42)), + s = _(n(45)); + t.isJSONObject = function(e) { + try { + var t = JSON.parse(e); + if (t && "object" === (void 0 === t ? "undefined" : (0, s.default)(t))) return t + } catch (e) {} + return !1 + }, t.objectify = function(e) { + return S(e) ? E(e) ? e.toJS() : e : {} + }, t.arrayify = function(e) { + return e ? e.toArray ? e.toArray() : x(e) : [] + }, t.fromJSOrdered = function e(t) { + if (E(t)) return t; + if (t instanceof y.default.File) return t; + return S(t) ? Array.isArray(t) ? l.default.Seq(t).map(e).toList() : l.default.OrderedMap(t).map(e) : t + }, t.bindToState = function(e, t) { + var n = {}; + return (0, u.default)(e).filter(function(t) { + return "function" == typeof e[t] + }).forEach(function(r) { + return n[r] = e[r].bind(null, t) + }), n + }, t.normalizeArray = x, t.isFn = function(e) { + return "function" == typeof e + }, t.isObject = S, t.isFunc = function(e) { + return "function" == typeof e + }, t.isArray = function(e) { + return Array.isArray(e) + }, t.objMap = function(e, t) { + return (0, u.default)(e).reduce(function(n, r) { + return n[r] = t(e[r], r), n + }, {}) + }, t.objReduce = function(e, t) { + return (0, u.default)(e).reduce(function(n, r) { + var o = t(e[r], r); + return o && "object" === (void 0 === o ? "undefined" : (0, s.default)(o)) && (0, a.default)(n, o), n + }, {}) + }, t.systemThunkMiddleware = function(e) { + return function(t) { + t.dispatch, t.getState; + return function(t) { + return function(n) { + return "function" == typeof n ? n(e()) : t(n) + } + } + } + }, t.defaultStatusCode = function(e) { + var t = e.keySeq(); + return t.contains(w) ? w : t.filter(function(e) { + return "2" === (e + "")[0] + }).sort().first() + }, t.getList = function(e, t) { + if (!l.default.Iterable.isIterable(e)) return l.default.List(); + var n = e.getIn(Array.isArray(t) ? t : [t]); + return l.default.List.isList(n) ? n : l.default.List() + }, t.highlight = function(e) { + var t = document; + if (!e) return ""; + if (e.textContent.length > 5e3) return e.textContent; + return function(e) { + for (var n, r, o, i, a, u = e.textContent, s = 0, l = u[0], c = 1, f = e.innerHTML = "", p = 0; r = n, n = p < 7 && "\\" == n ? 1 : c;) { + if (c = l, l = u[++s], i = f.length > 1, !c || p > 8 && "\n" == c || [/\S/.test(c), 1, 1, !/[$\w]/.test(c), ("/" == n || "\n" == n) && i, '"' == n && i, "'" == n && i, u[s - 4] + r + n == "--\x3e", r + n == "*/"][p]) + for (f && (e.appendChild(a = t.createElement("span")).setAttribute("style", ["color: #555; font-weight: bold;", "", "", "color: #555;", ""][p ? p < 3 ? 2 : p > 6 ? 4 : p > 3 ? 3 : +/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(f) : 0]), a.appendChild(t.createTextNode(f))), o = p && p < 7 ? p : o, f = "", p = 11; ![1, /[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(c), /[\])]/.test(c), /[$\w]/.test(c), "/" == c && o < 2 && "<" != n, '"' == c, "'" == c, c + l + u[s + 1] + u[s + 2] == "\x3c!--", c + l == "/*", c + l == "//", "#" == c][--p];); + f += c + } + }(e) + }, t.mapToList = function e(t) { + var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "key"; + var r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : l.default.Map(); + if (!l.default.Map.isMap(t) || !t.size) return l.default.List(); + Array.isArray(n) || (n = [n]); + if (n.length < 1) return t.merge(r); + var a = l.default.List(); + var u = n[0]; + var s = !0; + var c = !1; + var f = void 0; + try { + for (var p, d = (0, i.default)(t.entries()); !(s = (p = d.next()).done); s = !0) { + var h = p.value, + v = (0, o.default)(h, 2), + m = v[0], + g = v[1], + y = e(g, n.slice(1), r.set(u, m)); + a = l.default.List.isList(y) ? a.concat(y) : a.push(y) + } + } catch (e) { + c = !0, f = e + } finally { + try { + !s && d.return && d.return() + } finally { + if (c) throw f + } + } + return a + }, t.extractFileNameFromContentDispositionHeader = function(e) { + var t = /filename="([^;]*);?"/i.exec(e); + null === t && (t = /filename=([^;]*);?/i.exec(e)); + if (null !== t && t.length > 1) return t[1]; + return null + }, t.pascalCase = C, t.pascalCaseFilename = function(e) { + return C(e.replace(/\.[^./]*$/, "")) + }, t.sanitizeUrl = function(e) { + if ("string" != typeof e || "" === e) return ""; + return (0, c.sanitizeUrl)(e) + }, t.getAcceptControllingResponse = function(e) { + if (!l.default.OrderedMap.isOrderedMap(e)) return null; + if (!e.size) return null; + var t = e.find(function(e, t) { + return t.startsWith("2") && (0, u.default)(e.get("content") || {}).length > 0 + }), + n = e.get("default") || l.default.OrderedMap(), + r = (n.get("content") || l.default.OrderedMap()).keySeq().toJS().length ? n : null; + return t || r + }, t.deeplyStripKey = function e(t, n) { + var r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : function() { + return !0 + }; + if ("object" !== (void 0 === t ? "undefined" : (0, s.default)(t)) || Array.isArray(t) || null === t || !n) return t; + var o = (0, a.default)({}, t); + (0, u.default)(o).forEach(function(t) { + t === n && r(o[t], t) ? delete o[t] : o[t] = e(o[t], n, r) + }); + return o + }, t.stringify = function(e) { + if ("string" == typeof e) return e; + e.toJS && (e = e.toJS()); + if ("object" === (void 0 === e ? "undefined" : (0, s.default)(e)) && null !== e) try { + return (0, r.default)(e, null, 2) + } catch (t) { + return String(e) + } + return e.toString() + }, t.numberToString = function(e) { + if ("number" == typeof e) return e.toString(); + return e + }; + var l = _(n(7)), + c = n(571), + f = _(n(572)), + p = _(n(280)), + d = _(n(284)), + h = _(n(287)), + v = _(n(650)), + m = _(n(105)), + g = n(192), + y = _(n(32)), + b = _(n(723)); + + function _(e) { + return e && e.__esModule ? e : { + default: e + } + } + var w = "default", + E = t.isImmutable = function(e) { + return l.default.Iterable.isIterable(e) + }; + + function x(e) { + return Array.isArray(e) ? e : [e] + } + + function S(e) { + return !!e && "object" === (void 0 === e ? "undefined" : (0, s.default)(e)) + } + t.memoize = d.default; + + function C(e) { + return (0, p.default)((0, f.default)(e)) + } + t.propChecker = function(e, t) { + var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : [], + r = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : []; + return (0, u.default)(e).length !== (0, u.default)(t).length || ((0, v.default)(e, function(e, n) { + if (r.includes(n)) return !1; + var o = t[n]; + return l.default.Iterable.isIterable(e) ? !l.default.is(e, o) : ("object" !== (void 0 === e ? "undefined" : (0, s.default)(e)) || "object" !== (void 0 === o ? "undefined" : (0, s.default)(o))) && e !== o + }) || n.some(function(n) { + return !(0, m.default)(e[n], t[n]) + })) + }; + var k = t.validateMaximum = function(e, t) { + if (e > t) return "Value must be less than Maximum" + }, + A = t.validateMinimum = function(e, t) { + if (e < t) return "Value must be greater than Minimum" + }, + O = t.validateNumber = function(e) { + if (!/^-?\d+(\.?\d+)?$/.test(e)) return "Value must be a number" + }, + P = t.validateInteger = function(e) { + if (!/^-?\d+$/.test(e)) return "Value must be an integer" + }, + T = t.validateFile = function(e) { + if (e && !(e instanceof y.default.File)) return "Value must be a file" + }, + M = t.validateBoolean = function(e) { + if ("true" !== e && "false" !== e && !0 !== e && !1 !== e) return "Value must be a boolean" + }, + I = t.validateString = function(e) { + if (e && "string" != typeof e) return "Value must be a string" + }, + j = t.validateDateTime = function(e) { + if (isNaN(Date.parse(e))) return "Value must be a DateTime" + }, + N = t.validateGuid = function(e) { + if (e = e.toString().toLowerCase(), !/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(e)) return "Value must be a Guid" + }, + R = t.validateMaxLength = function(e, t) { + if (e.length > t) return "Value must be less than MaxLength" + }, + D = t.validateMinLength = function(e, t) { + if (e.length < t) return "Value must be greater than MinLength" + }, + L = t.validatePattern = function(e, t) { + if (!new RegExp(t).test(e)) return "Value must follow pattern " + t + }; + t.validateParam = function(e, t) { + var n = arguments.length > 2 && void 0 !== arguments[2] && arguments[2], + r = [], + o = t && "body" === e.get("in") ? e.get("value_xml") : e.get("value"), + i = e.get("required"), + a = n ? e.get("schema") : e; + if (!a) return r; + var u = a.get("maximum"), + c = a.get("minimum"), + f = a.get("type"), + p = a.get("format"), + d = a.get("maxLength"), + h = a.get("minLength"), + v = a.get("pattern"); + if (f && (i || o)) { + var m = "string" === f && o, + g = "array" === f && Array.isArray(o) && o.length, + b = "array" === f && l.default.List.isList(o) && o.count(), + _ = "file" === f && o instanceof y.default.File, + w = "boolean" === f && (o || !1 === o), + E = "number" === f && (o || 0 === o), + x = "integer" === f && (o || 0 === o), + S = !1; + if (n && "object" === f) + if ("object" === (void 0 === o ? "undefined" : (0, s.default)(o))) S = !0; + else if ("string" == typeof o) try { + JSON.parse(o), S = !0 + } catch (e) { + return r.push("Parameter string value must be valid JSON"), r + } + var C = [m, g, b, _, w, E, x, S].some(function(e) { + return !!e + }); + if (i && !C) return r.push("Required field is not provided"), r; + if (v) { + var U = L(o, v); + U && r.push(U) + } + if (d || 0 === d) { + var q = R(o, d); + q && r.push(q) + } + if (h) { + var F = D(o, h); + F && r.push(F) + } + if (u || 0 === u) { + var z = k(o, u); + z && r.push(z) + } + if (c || 0 === c) { + var B = A(o, c); + B && r.push(B) + } + if ("string" === f) { + var V = void 0; + if (!(V = "date-time" === p ? j(o) : "uuid" === p ? N(o) : I(o))) return r; + r.push(V) + } else if ("boolean" === f) { + var H = M(o); + if (!H) return r; + r.push(H) + } else if ("number" === f) { + var W = O(o); + if (!W) return r; + r.push(W) + } else if ("integer" === f) { + var J = P(o); + if (!J) return r; + r.push(J) + } else if ("array" === f) { + var Y; + if (!b || !o.count()) return r; + Y = a.getIn(["items", "type"]), o.forEach(function(e, t) { + var n = void 0; + "number" === Y ? n = O(e) : "integer" === Y ? n = P(e) : "string" === Y && (n = I(e)), n && r.push({ + index: t, + error: n + }) + }) + } else if ("file" === f) { + var K = T(o); + if (!K) return r; + r.push(K) + } + } + return r + }, t.getSampleSchema = function(e) { + var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "", + n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}; + if (/xml/.test(t)) { + if (!e.xml || !e.xml.name) { + if (e.xml = e.xml || {}, !e.$$ref) return e.type || e.items || e.properties || e.additionalProperties ? '\n\x3c!-- XML example cannot be generated --\x3e' : null; + var o = e.$$ref.match(/\S*\/(\S+)$/); + e.xml.name = o[1] + } + return (0, g.memoizedCreateXMLExample)(e, n) + } + var i = (0, g.memoizedSampleFromSchema)(e, n); + return "object" === (void 0 === i ? "undefined" : (0, s.default)(i)) ? (0, r.default)(i, null, 2) : i + }, t.parseSearch = function() { + var e = {}, + t = y.default.location.search; + if (!t) return {}; + if ("" != t) { + var n = t.substr(1).split("&"); + for (var r in n) n.hasOwnProperty(r) && (r = n[r].split("="), e[decodeURIComponent(r[0])] = r[1] && decodeURIComponent(r[1]) || "") + } + return e + }, t.serializeSearch = function(e) { + return (0, u.default)(e).map(function(t) { + return encodeURIComponent(t) + "=" + encodeURIComponent(e[t]) + }).join("&") + }, t.btoa = function(t) { + return (t instanceof e ? t : new e(t.toString(), "utf-8")).toString("base64") + }, t.sorters = { + operationsSorter: { + alpha: function(e, t) { + return e.get("path").localeCompare(t.get("path")) + }, + method: function(e, t) { + return e.get("method").localeCompare(t.get("method")) + } + }, + tagsSorter: { + alpha: function(e, t) { + return e.localeCompare(t) + } + } + }, t.buildFormData = function(e) { + var t = []; + for (var n in e) { + var r = e[n]; + void 0 !== r && "" !== r && t.push([n, "=", encodeURIComponent(r).replace(/%20/g, "+")].join("")) + } + return t.join("&") + }, t.shallowEqualKeys = function(e, t, n) { + return !!(0, h.default)(n, function(n) { + return (0, m.default)(e[n], t[n]) + }) + }; + var U = t.createDeepLinkPath = function(e) { + return "string" == typeof e || e instanceof String ? e.trim().replace(/\s/g, "_") : "" + }; + t.escapeDeepLinkPath = function(e) { + return (0, b.default)(U(e)) + }, t.getExtensions = function(e) { + return e.filter(function(e, t) { + return /^x-/.test(t) + }) + }, t.getCommonExtensions = function(e) { + return e.filter(function(e, t) { + return /^pattern|maxLength|minLength|maximum|minimum/.test(t) + }) + } + }).call(t, n(54).Buffer) + }, function(e, t, n) { + "use strict"; + e.exports = function(e) { + for (var t = arguments.length - 1, n = "Minified React error #" + e + "; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=" + e, r = 0; r < t; r++) n += "&args[]=" + encodeURIComponent(arguments[r + 1]); + n += " for the full message or use the non-minified dev environment for full errors and additional helpful warnings."; + var o = new Error(n); + throw o.name = "Invariant Violation", o.framesToPop = 1, o + } + }, function(e, t, n) { + "use strict"; + var r = n(7), + o = "<>", + i = { + listOf: function(e) { + return l(e, "List", r.List.isList) + }, + mapOf: function(e, t) { + return c(e, t, "Map", r.Map.isMap) + }, + orderedMapOf: function(e, t) { + return c(e, t, "OrderedMap", r.OrderedMap.isOrderedMap) + }, + setOf: function(e) { + return l(e, "Set", r.Set.isSet) + }, + orderedSetOf: function(e) { + return l(e, "OrderedSet", r.OrderedSet.isOrderedSet) + }, + stackOf: function(e) { + return l(e, "Stack", r.Stack.isStack) + }, + iterableOf: function(e) { + return l(e, "Iterable", r.Iterable.isIterable) + }, + recordOf: function(e) { + return u(function(t, n, o, i, u) { + for (var s = arguments.length, l = Array(s > 5 ? s - 5 : 0), c = 5; c < s; c++) l[c - 5] = arguments[c]; + var f = t[n]; + if (!(f instanceof r.Record)) { + var p = a(f), + d = i; + return new Error("Invalid " + d + " `" + u + "` of type `" + p + "` supplied to `" + o + "`, expected an Immutable.js Record.") + } + for (var h in e) { + var v = e[h]; + if (v) { + var m = f.toObject(), + g = v.apply(void 0, [m, h, o, i, u + "." + h].concat(l)); + if (g) return g + } + } + }) + }, + shape: p, + contains: p, + mapContains: function(e) { + return f(e, "Map", r.Map.isMap) + }, + list: s("List", r.List.isList), + map: s("Map", r.Map.isMap), + orderedMap: s("OrderedMap", r.OrderedMap.isOrderedMap), + set: s("Set", r.Set.isSet), + orderedSet: s("OrderedSet", r.OrderedSet.isOrderedSet), + stack: s("Stack", r.Stack.isStack), + seq: s("Seq", r.Seq.isSeq), + record: s("Record", function(e) { + return e instanceof r.Record + }), + iterable: s("Iterable", r.Iterable.isIterable) + }; + + function a(e) { + var t = typeof e; + return Array.isArray(e) ? "array" : e instanceof RegExp ? "object" : e instanceof r.Iterable ? "Immutable." + e.toSource().split(" ")[0] : t + } + + function u(e) { + function t(t, n, r, i, a, u) { + for (var s = arguments.length, l = Array(s > 6 ? s - 6 : 0), c = 6; c < s; c++) l[c - 6] = arguments[c]; + return u = u || r, i = i || o, null != n[r] ? e.apply(void 0, [n, r, i, a, u].concat(l)) : t ? new Error("Required " + a + " `" + u + "` was not specified in `" + i + "`.") : void 0 + } + var n = t.bind(null, !1); + return n.isRequired = t.bind(null, !0), n + } + + function s(e, t) { + return u(function(n, r, o, i, u) { + var s = n[r]; + if (!t(s)) { + var l = a(s); + return new Error("Invalid " + i + " `" + u + "` of type `" + l + "` supplied to `" + o + "`, expected `" + e + "`.") + } + return null + }) + } + + function l(e, t, n) { + return u(function(r, o, i, u, s) { + for (var l = arguments.length, c = Array(l > 5 ? l - 5 : 0), f = 5; f < l; f++) c[f - 5] = arguments[f]; + var p = r[o]; + if (!n(p)) { + var d = u, + h = a(p); + return new Error("Invalid " + d + " `" + s + "` of type `" + h + "` supplied to `" + i + "`, expected an Immutable.js " + t + ".") + } + if ("function" != typeof e) return new Error("Invalid typeChecker supplied to `" + i + "` for propType `" + s + "`, expected a function."); + for (var v = p.toArray(), m = 0, g = v.length; m < g; m++) { + var y = e.apply(void 0, [v, m, i, u, s + "[" + m + "]"].concat(c)); + if (y instanceof Error) return y + } + }) + } + + function c(e, t, n, r) { + return u(function() { + for (var o = arguments.length, i = Array(o), a = 0; a < o; a++) i[a] = arguments[a]; + return l(e, n, r).apply(void 0, i) || t && (s = t, u(function(e, t, n, r, o) { + for (var i = arguments.length, a = Array(i > 5 ? i - 5 : 0), u = 5; u < i; u++) a[u - 5] = arguments[u]; + var l = e[t]; + if ("function" != typeof s) return new Error("Invalid keysTypeChecker (optional second argument) supplied to `" + n + "` for propType `" + o + "`, expected a function."); + for (var c = l.keySeq().toArray(), f = 0, p = c.length; f < p; f++) { + var d = s.apply(void 0, [c, f, n, r, o + " -> key(" + c[f] + ")"].concat(a)); + if (d instanceof Error) return d + } + })).apply(void 0, i); + var s + }) + } + + function f(e) { + var t = void 0 === arguments[1] ? "Iterable" : arguments[1], + n = void 0 === arguments[2] ? r.Iterable.isIterable : arguments[2]; + return u(function(r, o, i, u, s) { + for (var l = arguments.length, c = Array(l > 5 ? l - 5 : 0), f = 5; f < l; f++) c[f - 5] = arguments[f]; + var p = r[o]; + if (!n(p)) { + var d = a(p); + return new Error("Invalid " + u + " `" + s + "` of type `" + d + "` supplied to `" + i + "`, expected an Immutable.js " + t + ".") + } + var h = p.toObject(); + for (var v in e) { + var m = e[v]; + if (m) { + var g = m.apply(void 0, [h, v, i, u, s + "." + v].concat(c)); + if (g) return g + } + } + }) + } + + function p(e) { + return f(e) + } + e.exports = i + }, function(e, t, n) { + "use strict"; + /* + object-assign + (c) Sindre Sorhus + @license MIT + */ + var r = Object.getOwnPropertySymbols, + o = Object.prototype.hasOwnProperty, + i = Object.prototype.propertyIsEnumerable; + e.exports = function() { + try { + if (!Object.assign) return !1; + var e = new String("abc"); + if (e[5] = "de", "5" === Object.getOwnPropertyNames(e)[0]) return !1; + for (var t = {}, n = 0; n < 10; n++) t["_" + String.fromCharCode(n)] = n; + if ("0123456789" !== Object.getOwnPropertyNames(t).map(function(e) { + return t[e] + }).join("")) return !1; + var r = {}; + return "abcdefghijklmnopqrst".split("").forEach(function(e) { + r[e] = e + }), "abcdefghijklmnopqrst" === Object.keys(Object.assign({}, r)).join("") + } catch (e) { + return !1 + } + }() ? Object.assign : function(e, t) { + for (var n, a, u = function(e) { + if (null === e || void 0 === e) throw new TypeError("Object.assign cannot be called with null or undefined"); + return Object(e) + }(e), s = 1; s < arguments.length; s++) { + for (var l in n = Object(arguments[s])) o.call(n, l) && (u[l] = n[l]); + if (r) { + a = r(n); + for (var c = 0; c < a.length; c++) i.call(n, a[c]) && (u[a[c]] = n[a[c]]) + } + } + return u + } + }, function(e, t, n) { + "use strict"; + var r = n(11), + o = n(87), + i = n(350), + a = (n(8), o.ID_ATTRIBUTE_NAME), + u = i, + s = "__reactInternalInstance$" + Math.random().toString(36).slice(2); + + function l(e, t) { + return 1 === e.nodeType && e.getAttribute(a) === String(t) || 8 === e.nodeType && e.nodeValue === " react-text: " + t + " " || 8 === e.nodeType && e.nodeValue === " react-empty: " + t + " " + } + + function c(e) { + for (var t; t = e._renderedComponent;) e = t; + return e + } + + function f(e, t) { + var n = c(e); + n._hostNode = t, t[s] = n + } + + function p(e, t) { + if (!(e._flags & u.hasCachedChildNodes)) { + var n = e._renderedChildren, + o = t.firstChild; + e: for (var i in n) + if (n.hasOwnProperty(i)) { + var a = n[i], + s = c(a)._domID; + if (0 !== s) { + for (; null !== o; o = o.nextSibling) + if (l(o, s)) { + f(a, o); + continue e + } + r("32", s) + } + } + e._flags |= u.hasCachedChildNodes + } + } + + function d(e) { + if (e[s]) return e[s]; + for (var t, n, r = []; !e[s];) { + if (r.push(e), !e.parentNode) return null; + e = e.parentNode + } + for (; e && (n = e[s]); e = r.pop()) t = n, r.length && p(n, e); + return t + } + var h = { + getClosestInstanceFromNode: d, + getInstanceFromNode: function(e) { + var t = d(e); + return null != t && t._hostNode === e ? t : null + }, + getNodeFromInstance: function(e) { + if (void 0 === e._hostNode && r("33"), e._hostNode) return e._hostNode; + for (var t = []; !e._hostNode;) t.push(e), e._hostParent || r("34"), e = e._hostParent; + for (; t.length; e = t.pop()) p(e, e._hostNode); + return e._hostNode + }, + precacheChildNodes: p, + precacheNode: f, + uncacheNode: function(e) { + var t = e._hostNode; + t && (delete t[s], e._hostNode = null) + } + }; + e.exports = h + }, function(e, t) { + var n = e.exports = { + version: "2.5.5" + }; + "number" == typeof __e && (__e = n) + }, function(e, t, n) { + "use strict"; + var r = n(107), + o = ["kind", "resolve", "construct", "instanceOf", "predicate", "represent", "defaultStyle", "styleAliases"], + i = ["scalar", "sequence", "mapping"]; + e.exports = function(e, t) { + var n, a; + if (t = t || {}, Object.keys(t).forEach(function(t) { + if (-1 === o.indexOf(t)) throw new r('Unknown option "' + t + '" is met in definition of "' + e + '" YAML type.') + }), this.tag = e, this.kind = t.kind || null, this.resolve = t.resolve || function() { + return !0 + }, this.construct = t.construct || function(e) { + return e + }, this.instanceOf = t.instanceOf || null, this.predicate = t.predicate || null, this.represent = t.represent || null, this.defaultStyle = t.defaultStyle || null, this.styleAliases = (n = t.styleAliases || null, a = {}, null !== n && Object.keys(n).forEach(function(e) { + n[e].forEach(function(t) { + a[String(t)] = e + }) + }), a), -1 === i.indexOf(this.kind)) throw new r('Unknown kind "' + this.kind + '" is specified for "' + e + '" YAML type.') + } + }, function(e, t, n) { + var r = n(242)("wks"), + o = n(167), + i = n(33).Symbol, + a = "function" == typeof i; + (e.exports = function(e) { + return r[e] || (r[e] = a && i[e] || (a ? i : o)("Symbol." + e)) + }).store = r + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r = i(n(568)), + o = i(n(91)); + + function i(e) { + return e && e.__esModule ? e : { + default: e + } + } + t.default = function() { + return function(e, t) { + if (Array.isArray(e)) return e; + if ((0, r.default)(Object(e))) return function(e, t) { + var n = [], + r = !0, + i = !1, + a = void 0; + try { + for (var u, s = (0, o.default)(e); !(r = (u = s.next()).done) && (n.push(u.value), !t || n.length !== t); r = !0); + } catch (e) { + i = !0, a = e + } finally { + try { + !r && s.return && s.return() + } finally { + if (i) throw a + } + } + return n + }(e, t); + throw new TypeError("Invalid attempt to destructure non-iterable instance") + } + }() + }, function(e, t, n) { + var r = n(162)("wks"), + o = n(116), + i = n(21).Symbol, + a = "function" == typeof i; + (e.exports = function(e) { + return r[e] || (r[e] = a && i[e] || (a ? i : o)("Symbol." + e)) + }).store = r + }, function(e, t, n) { + var r = n(21), + o = n(15), + i = n(49), + a = n(50), + u = n(52), + s = function(e, t, n) { + var l, c, f, p = e & s.F, + d = e & s.G, + h = e & s.S, + v = e & s.P, + m = e & s.B, + g = e & s.W, + y = d ? o : o[t] || (o[t] = {}), + b = y.prototype, + _ = d ? r : h ? r[t] : (r[t] || {}).prototype; + for (l in d && (n = t), n)(c = !p && _ && void 0 !== _[l]) && u(y, l) || (f = c ? _[l] : n[l], y[l] = d && "function" != typeof _[l] ? n[l] : m && c ? i(f, r) : g && _[l] == f ? function(e) { + var t = function(t, n, r) { + if (this instanceof e) { + switch (arguments.length) { + case 0: + return new e; + case 1: + return new e(t); + case 2: + return new e(t, n) + } + return new e(t, n, r) + } + return e.apply(this, arguments) + }; + return t.prototype = e.prototype, t + }(f) : v && "function" == typeof f ? i(Function.call, f) : f, v && ((y.virtual || (y.virtual = {}))[l] = f, e & s.R && b && !b[l] && a(b, l, f))) + }; + s.F = 1, s.G = 2, s.S = 4, s.P = 8, s.B = 16, s.W = 32, s.U = 64, s.R = 128, e.exports = s + }, function(e, t) { + var n = e.exports = "undefined" != typeof window && window.Math == Math ? window : "undefined" != typeof self && self.Math == Math ? self : Function("return this")(); + "number" == typeof __g && (__g = n) + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r, o = n(262), + i = (r = o) && r.__esModule ? r : { + default: r + }; + t.default = function(e, t, n) { + return t in e ? (0, i.default)(e, t, { + value: n, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[t] = n, e + } + }, function(e, t, n) { + e.exports = { + default: n(533), + __esModule: !0 + } + }, function(e, t) { + var n = Array.isArray; + e.exports = n + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r, o = n(23), + i = (r = o) && r.__esModule ? r : { + default: r + }; + t.default = i.default || function(e) { + for (var t = 1; t < arguments.length; t++) { + var n = arguments[t]; + for (var r in n) Object.prototype.hasOwnProperty.call(n, r) && (e[r] = n[r]) + } + return e + } + }, function(e, t, n) { + "use strict"; + var r = !("undefined" == typeof window || !window.document || !window.document.createElement), + o = { + canUseDOM: r, + canUseWorkers: "undefined" != typeof Worker, + canUseEventListeners: r && !(!window.addEventListener && !window.attachEvent), + canUseViewport: r && !!window.screen, + isInWorker: !r + }; + e.exports = o + }, function(e, t, n) { + "use strict"; + var r = Object.prototype.hasOwnProperty; + + function o(e, t) { + return !!e && r.call(e, t) + } + var i = /\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + + function a(e) { + return !(e >= 55296 && e <= 57343) && (!(e >= 64976 && e <= 65007) && (65535 != (65535 & e) && 65534 != (65535 & e) && (!(e >= 0 && e <= 8) && (11 !== e && (!(e >= 14 && e <= 31) && (!(e >= 127 && e <= 159) && !(e > 1114111))))))) + } + + function u(e) { + if (e > 65535) { + var t = 55296 + ((e -= 65536) >> 10), + n = 56320 + (1023 & e); + return String.fromCharCode(t, n) + } + return String.fromCharCode(e) + } + var s = /&([a-z#][a-z0-9]{1,31});/gi, + l = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i, + c = n(416); + + function f(e, t) { + var n = 0; + return o(c, t) ? c[t] : 35 === t.charCodeAt(0) && l.test(t) && a(n = "x" === t[1].toLowerCase() ? parseInt(t.slice(2), 16) : parseInt(t.slice(1), 10)) ? u(n) : e + } + var p = /[&<>"]/, + d = /[&<>"]/g, + h = { + "&": "&", + "<": "<", + ">": ">", + '"': """ + }; + + function v(e) { + return h[e] + } + t.assign = function(e) { + return [].slice.call(arguments, 1).forEach(function(t) { + if (t) { + if ("object" != typeof t) throw new TypeError(t + "must be object"); + Object.keys(t).forEach(function(n) { + e[n] = t[n] + }) + } + }), e + }, t.isString = function(e) { + return "[object String]" === function(e) { + return Object.prototype.toString.call(e) + }(e) + }, t.has = o, t.unescapeMd = function(e) { + return e.indexOf("\\") < 0 ? e : e.replace(i, "$1") + }, t.isValidEntityCode = a, t.fromCodePoint = u, t.replaceEntities = function(e) { + return e.indexOf("&") < 0 ? e : e.replace(s, f) + }, t.escapeHtml = function(e) { + return p.test(e) ? e.replace(d, v) : e + } + }, function(e, t) { + e.exports = function(e) { + return "object" == typeof e ? null !== e : "function" == typeof e + } + }, function(e, t, n) { + var r = n(33), + o = n(60), + i = n(58), + a = n(73), + u = n(120), + s = function(e, t, n) { + var l, c, f, p, d = e & s.F, + h = e & s.G, + v = e & s.S, + m = e & s.P, + g = e & s.B, + y = h ? r : v ? r[t] || (r[t] = {}) : (r[t] || {}).prototype, + b = h ? o : o[t] || (o[t] = {}), + _ = b.prototype || (b.prototype = {}); + for (l in h && (n = t), n) f = ((c = !d && y && void 0 !== y[l]) ? y : n)[l], p = g && c ? u(f, r) : m && "function" == typeof f ? u(Function.call, f) : f, y && a(y, l, f, e & s.U), b[l] != f && i(b, l, p), m && _[l] != f && (_[l] = f) + }; + r.core = o, s.F = 1, s.G = 2, s.S = 4, s.P = 8, s.B = 16, s.W = 32, s.U = 64, s.R = 128, e.exports = s + }, function(e, t, n) { + var r = n(29), + o = n(101), + i = n(53), + a = /"/g, + u = function(e, t, n, r) { + var o = String(i(e)), + u = "<" + t; + return "" !== n && (u += " " + n + '="' + String(r).replace(a, """) + '"'), u + ">" + o + "" + }; + e.exports = function(e, t) { + var n = {}; + n[e] = t(u), r(r.P + r.F * o(function() { + var t = "" [e]('"'); + return t !== t.toLowerCase() || t.split('"').length > 3 + }), "String", n) + } + }, function(e, t) { + var n; + n = function() { + return this + }(); + try { + n = n || Function("return this")() || (0, eval)("this") + } catch (e) { + "object" == typeof window && (n = window) + } + e.exports = n + }, function(e, t, n) { + "use strict"; + var r, o = n(91), + i = (r = o) && r.__esModule ? r : { + default: r + }; + e.exports = function() { + var e = { + location: {}, + history: {}, + open: function() {}, + close: function() {}, + File: function() {} + }; + if ("undefined" == typeof window) return e; + try { + e = window; + var t = !0, + n = !1, + r = void 0; + try { + for (var o, a = (0, i.default)(["File", "Blob", "FormData"]); !(t = (o = a.next()).done); t = !0) { + var u = o.value; + u in window && (e[u] = window[u]) + } + } catch (e) { + n = !0, r = e + } finally { + try { + !t && a.return && a.return() + } finally { + if (n) throw r + } + } + } catch (e) { + console.error(e) + } + return e + }() + }, function(e, t) { + var n = e.exports = "undefined" != typeof window && window.Math == Math ? window : "undefined" != typeof self && self.Math == Math ? self : Function("return this")(); + "number" == typeof __g && (__g = n) + }, function(e, t, n) { + "use strict"; + + function r(e) { + return function() { + return e + } + } + var o = function() {}; + o.thatReturns = r, o.thatReturnsFalse = r(!1), o.thatReturnsTrue = r(!0), o.thatReturnsNull = r(null), o.thatReturnsThis = function() { + return this + }, o.thatReturnsArgument = function(e) { + return e + }, e.exports = o + }, function(e, t, n) { + "use strict"; + Object.defineProperty(t, "__esModule", { + value: !0 + }); + var r = i(n(25)); + t.isOAS3 = a, t.isSwagger2 = function(e) { + var t = e.get("swagger"); + if ("string" != typeof t) return !1; + return t.startsWith("2.0") + }, t.OAS3ComponentWrapFactory = function(e) { + return function(t, n) { + return function(i) { + if (n && n.specSelectors && n.specSelectors.specJson) { + var u = n.specSelectors.specJson(); + return a(u) ? o.default.createElement(e, (0, r.default)({}, i, n, { + Ori: t + })) : o.default.createElement(t, i) + } + return console.warn("OAS3 wrapper: couldn't get spec"), null + } + } + }; + var o = i(n(0)); + + function i(e) { + return e && e.__esModule ? e : { + default: e + } + } + + function a(e) { + var t = e.get("openapi"); + return "string" == typeof t && (t.startsWith("3.0.") && t.length > 4) + } + }, function(e, t, n) { + var r = n(28); + e.exports = function(e) { + if (!r(e)) throw TypeError(e + " is not an object!"); + return e + } + }, function(e, t, n) { + var r = n(278), + o = "object" == typeof self && self && self.Object === Object && self, + i = r || o || Function("return this")(); + e.exports = i + }, function(e, t) { + e.exports = function(e) { + var t = typeof e; + return null != e && ("object" == t || "function" == t) + } + }, function(e, t, n) { + "use strict"; + var r = null; + e.exports = { + debugTool: r + } + }, function(e, t, n) { + var r = n(36), + o = n(238), + i = n(157), + a = Object.defineProperty; + t.f = n(44) ? Object.defineProperty : function(e, t, n) { + if (r(e), t = i(t, !0), r(n), o) try { + return a(e, t, n) + } catch (e) {} + if ("get" in n || "set" in n) throw TypeError("Accessors not supported!"); + return "value" in n && (e[t] = n.value), e + } + }, function(e, t, n) { + e.exports = { + default: n(516), + __esModule: !0 + } + }, function(e, t, n) { + e.exports = { + default: n(517), + __esModule: !0 + } + }, function(e, t, n) { + "use strict"; + var r = n(11), + o = n(13), + i = n(354), + a = n(69), + u = n(355), + s = n(88), + l = n(147), + c = n(8), + f = [], + p = 0, + d = i.getPooled(), + h = !1, + v = null; + + function m() { + E.ReactReconcileTransaction && v || r("123") + } + var g = [{ + initialize: function() { + this.dirtyComponentsLength = f.length + }, + close: function() { + this.dirtyComponentsLength !== f.length ? (f.splice(0, this.dirtyComponentsLength), w()) : f.length = 0 + } + }, { + initialize: function() { + this.callbackQueue.reset() + }, + close: function() { + this.callbackQueue.notifyAll() + } + }]; + + function y() { + this.reinitializeTransaction(), this.dirtyComponentsLength = null, this.callbackQueue = i.getPooled(), this.reconcileTransaction = E.ReactReconcileTransaction.getPooled(!0) + } + + function b(e, t) { + return e._mountOrder - t._mountOrder + } + + function _(e) { + var t = e.dirtyComponentsLength; + t !== f.length && r("124", t, f.length), f.sort(b), p++; + for (var n = 0; n < t; n++) { + var o, i = f[n], + a = i._pendingCallbacks; + if (i._pendingCallbacks = null, u.logTopLevelRenders) { + var l = i; + i._currentElement.type.isReactTopLevelWrapper && (l = i._renderedComponent), o = "React update: " + l.getName(), console.time(o) + } + if (s.performUpdateIfNecessary(i, e.reconcileTransaction, p), o && console.timeEnd(o), a) + for (var c = 0; c < a.length; c++) e.callbackQueue.enqueue(a[c], i.getPublicInstance()) + } + } + o(y.prototype, l, { + getTransactionWrappers: function() { + return g + }, + destructor: function() { + this.dirtyComponentsLength = null, i.release(this.callbackQueue), this.callbackQueue = null, E.ReactReconcileTransaction.release(this.reconcileTransaction), this.reconcileTransaction = null + }, + perform: function(e, t, n) { + return l.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, e, t, n) + } + }), a.addPoolingTo(y); + var w = function() { + for (; f.length || h;) { + if (f.length) { + var e = y.getPooled(); + e.perform(_, null, e), y.release(e) + } + if (h) { + h = !1; + var t = d; + d = i.getPooled(), t.notifyAll(), i.release(t) + } + } + }; + var E = { + ReactReconcileTransaction: null, + batchedUpdates: function(e, t, n, r, o, i) { + return m(), v.batchedUpdates(e, t, n, r, o, i) + }, + enqueueUpdate: function e(t) { + m(), v.isBatchingUpdates ? (f.push(t), null == t._updateBatchNumber && (t._updateBatchNumber = p + 1)) : v.batchedUpdates(e, t) + }, + flushBatchedUpdates: w, + injection: { + injectReconcileTransaction: function(e) { + e || r("126"), E.ReactReconcileTransaction = e + }, + injectBatchingStrategy: function(e) { + e || r("127"), "function" != typeof e.batchedUpdates && r("128"), "boolean" != typeof e.isBatchingUpdates && r("129"), v = e + } + }, + asap: function(e, t) { + c(v.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched."), d.enqueue(e, t), h = !0 + } + }; + e.exports = E + }, function(e, t, n) { + e.exports = !n(51)(function() { + return 7 != Object.defineProperty({}, "a", { + get: function() { + return 7 + } + }).a + }) + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r = a(n(519)), + o = a(n(521)), + i = "function" == typeof o.default && "symbol" == typeof r.default ? function(e) { + return typeof e + } : function(e) { + return e && "function" == typeof o.default && e.constructor === o.default && e !== o.default.prototype ? "symbol" : typeof e + }; + + function a(e) { + return e && e.__esModule ? e : { + default: e + } + } + t.default = "function" == typeof o.default && "symbol" === i(r.default) ? function(e) { + return void 0 === e ? "undefined" : i(e) + } : function(e) { + return e && "function" == typeof o.default && e.constructor === o.default && e !== o.default.prototype ? "symbol" : void 0 === e ? "undefined" : i(e) + } + }, function(e, t, n) { + "use strict"; + e.exports = { + current: null + } + }, function(e, t) { + e.exports = function(e) { + return null != e && "object" == typeof e + } + }, function(e, t, n) { + "use strict"; + var r = n(13), + o = n(69), + i = n(34), + a = (n(9), ["dispatchConfig", "_targetInst", "nativeEvent", "isDefaultPrevented", "isPropagationStopped", "_dispatchListeners", "_dispatchInstances"]), + u = { + type: null, + target: null, + currentTarget: i.thatReturnsNull, + eventPhase: null, + bubbles: null, + cancelable: null, + timeStamp: function(e) { + return e.timeStamp || Date.now() + }, + defaultPrevented: null, + isTrusted: null + }; + + function s(e, t, n, r) { + this.dispatchConfig = e, this._targetInst = t, this.nativeEvent = n; + var o = this.constructor.Interface; + for (var a in o) + if (o.hasOwnProperty(a)) { + 0; + var u = o[a]; + u ? this[a] = u(n) : "target" === a ? this.target = r : this[a] = n[a] + } + var s = null != n.defaultPrevented ? n.defaultPrevented : !1 === n.returnValue; + return this.isDefaultPrevented = s ? i.thatReturnsTrue : i.thatReturnsFalse, this.isPropagationStopped = i.thatReturnsFalse, this + } + r(s.prototype, { + preventDefault: function() { + this.defaultPrevented = !0; + var e = this.nativeEvent; + e && (e.preventDefault ? e.preventDefault() : "unknown" != typeof e.returnValue && (e.returnValue = !1), this.isDefaultPrevented = i.thatReturnsTrue) + }, + stopPropagation: function() { + var e = this.nativeEvent; + e && (e.stopPropagation ? e.stopPropagation() : "unknown" != typeof e.cancelBubble && (e.cancelBubble = !0), this.isPropagationStopped = i.thatReturnsTrue) + }, + persist: function() { + this.isPersistent = i.thatReturnsTrue + }, + isPersistent: i.thatReturnsFalse, + destructor: function() { + var e = this.constructor.Interface; + for (var t in e) this[t] = null; + for (var n = 0; n < a.length; n++) this[a[n]] = null + } + }), s.Interface = u, s.augmentClass = function(e, t) { + var n = function() {}; + n.prototype = this.prototype; + var i = new n; + r(i, e.prototype), e.prototype = i, e.prototype.constructor = e, e.Interface = r({}, this.Interface, t), e.augmentClass = this.augmentClass, o.addPoolingTo(e, o.fourArgumentPooler) + }, o.addPoolingTo(s, o.fourArgumentPooler), e.exports = s + }, function(e, t, n) { + var r = n(94); + e.exports = function(e, t, n) { + if (r(e), void 0 === t) return e; + switch (n) { + case 1: + return function(n) { + return e.call(t, n) + }; + case 2: + return function(n, r) { + return e.call(t, n, r) + }; + case 3: + return function(n, r, o) { + return e.call(t, n, r, o) + } + } + return function() { + return e.apply(t, arguments) + } + } + }, function(e, t, n) { + var r = n(40), + o = n(95); + e.exports = n(44) ? function(e, t, n) { + return r.f(e, t, o(1, n)) + } : function(e, t, n) { + return e[t] = n, e + } + }, function(e, t) { + e.exports = function(e) { + try { + return !!e() + } catch (e) { + return !0 + } + } + }, function(e, t) { + var n = {}.hasOwnProperty; + e.exports = function(e, t) { + return n.call(e, t) + } + }, function(e, t) { + e.exports = function(e) { + if (void 0 == e) throw TypeError("Can't call method on " + e); + return e + } + }, function(e, t, n) { + "use strict"; + (function(e) { + /*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ + var r = n(528), + o = n(529), + i = n(261); + + function a() { + return s.TYPED_ARRAY_SUPPORT ? 2147483647 : 1073741823 + } + + function u(e, t) { + if (a() < t) throw new RangeError("Invalid typed array length"); + return s.TYPED_ARRAY_SUPPORT ? (e = new Uint8Array(t)).__proto__ = s.prototype : (null === e && (e = new s(t)), e.length = t), e + } + + function s(e, t, n) { + if (!(s.TYPED_ARRAY_SUPPORT || this instanceof s)) return new s(e, t, n); + if ("number" == typeof e) { + if ("string" == typeof t) throw new Error("If encoding is specified then the first argument must be a string"); + return f(this, e) + } + return l(this, e, t, n) + } + + function l(e, t, n, r) { + if ("number" == typeof t) throw new TypeError('"value" argument must not be a number'); + return "undefined" != typeof ArrayBuffer && t instanceof ArrayBuffer ? function(e, t, n, r) { + if (t.byteLength, n < 0 || t.byteLength < n) throw new RangeError("'offset' is out of bounds"); + if (t.byteLength < n + (r || 0)) throw new RangeError("'length' is out of bounds"); + t = void 0 === n && void 0 === r ? new Uint8Array(t) : void 0 === r ? new Uint8Array(t, n) : new Uint8Array(t, n, r); + s.TYPED_ARRAY_SUPPORT ? (e = t).__proto__ = s.prototype : e = p(e, t); + return e + }(e, t, n, r) : "string" == typeof t ? function(e, t, n) { + "string" == typeof n && "" !== n || (n = "utf8"); + if (!s.isEncoding(n)) throw new TypeError('"encoding" must be a valid string encoding'); + var r = 0 | h(t, n), + o = (e = u(e, r)).write(t, n); + o !== r && (e = e.slice(0, o)); + return e + }(e, t, n) : function(e, t) { + if (s.isBuffer(t)) { + var n = 0 | d(t.length); + return 0 === (e = u(e, n)).length ? e : (t.copy(e, 0, 0, n), e) + } + if (t) { + if ("undefined" != typeof ArrayBuffer && t.buffer instanceof ArrayBuffer || "length" in t) return "number" != typeof t.length || (r = t.length) != r ? u(e, 0) : p(e, t); + if ("Buffer" === t.type && i(t.data)) return p(e, t.data) + } + var r; + throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.") + }(e, t) + } + + function c(e) { + if ("number" != typeof e) throw new TypeError('"size" argument must be a number'); + if (e < 0) throw new RangeError('"size" argument must not be negative') + } + + function f(e, t) { + if (c(t), e = u(e, t < 0 ? 0 : 0 | d(t)), !s.TYPED_ARRAY_SUPPORT) + for (var n = 0; n < t; ++n) e[n] = 0; + return e + } + + function p(e, t) { + var n = t.length < 0 ? 0 : 0 | d(t.length); + e = u(e, n); + for (var r = 0; r < n; r += 1) e[r] = 255 & t[r]; + return e + } + + function d(e) { + if (e >= a()) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + a().toString(16) + " bytes"); + return 0 | e + } + + function h(e, t) { + if (s.isBuffer(e)) return e.length; + if ("undefined" != typeof ArrayBuffer && "function" == typeof ArrayBuffer.isView && (ArrayBuffer.isView(e) || e instanceof ArrayBuffer)) return e.byteLength; + "string" != typeof e && (e = "" + e); + var n = e.length; + if (0 === n) return 0; + for (var r = !1;;) switch (t) { + case "ascii": + case "latin1": + case "binary": + return n; + case "utf8": + case "utf-8": + case void 0: + return F(e).length; + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return 2 * n; + case "hex": + return n >>> 1; + case "base64": + return z(e).length; + default: + if (r) return F(e).length; + t = ("" + t).toLowerCase(), r = !0 + } + } + + function v(e, t, n) { + var r = e[t]; + e[t] = e[n], e[n] = r + } + + function m(e, t, n, r, o) { + if (0 === e.length) return -1; + if ("string" == typeof n ? (r = n, n = 0) : n > 2147483647 ? n = 2147483647 : n < -2147483648 && (n = -2147483648), n = +n, isNaN(n) && (n = o ? 0 : e.length - 1), n < 0 && (n = e.length + n), n >= e.length) { + if (o) return -1; + n = e.length - 1 + } else if (n < 0) { + if (!o) return -1; + n = 0 + } + if ("string" == typeof t && (t = s.from(t, r)), s.isBuffer(t)) return 0 === t.length ? -1 : g(e, t, n, r, o); + if ("number" == typeof t) return t &= 255, s.TYPED_ARRAY_SUPPORT && "function" == typeof Uint8Array.prototype.indexOf ? o ? Uint8Array.prototype.indexOf.call(e, t, n) : Uint8Array.prototype.lastIndexOf.call(e, t, n) : g(e, [t], n, r, o); + throw new TypeError("val must be string, number or Buffer") + } + + function g(e, t, n, r, o) { + var i, a = 1, + u = e.length, + s = t.length; + if (void 0 !== r && ("ucs2" === (r = String(r).toLowerCase()) || "ucs-2" === r || "utf16le" === r || "utf-16le" === r)) { + if (e.length < 2 || t.length < 2) return -1; + a = 2, u /= 2, s /= 2, n /= 2 + } + + function l(e, t) { + return 1 === a ? e[t] : e.readUInt16BE(t * a) + } + if (o) { + var c = -1; + for (i = n; i < u; i++) + if (l(e, i) === l(t, -1 === c ? 0 : i - c)) { + if (-1 === c && (c = i), i - c + 1 === s) return c * a + } else -1 !== c && (i -= i - c), c = -1 + } else + for (n + s > u && (n = u - s), i = n; i >= 0; i--) { + for (var f = !0, p = 0; p < s; p++) + if (l(e, i + p) !== l(t, p)) { + f = !1; + break + } + if (f) return i + } + return -1 + } + + function y(e, t, n, r) { + n = Number(n) || 0; + var o = e.length - n; + r ? (r = Number(r)) > o && (r = o) : r = o; + var i = t.length; + if (i % 2 != 0) throw new TypeError("Invalid hex string"); + r > i / 2 && (r = i / 2); + for (var a = 0; a < r; ++a) { + var u = parseInt(t.substr(2 * a, 2), 16); + if (isNaN(u)) return a; + e[n + a] = u + } + return a + } + + function b(e, t, n, r) { + return B(F(t, e.length - n), e, n, r) + } + + function _(e, t, n, r) { + return B(function(e) { + for (var t = [], n = 0; n < e.length; ++n) t.push(255 & e.charCodeAt(n)); + return t + }(t), e, n, r) + } + + function w(e, t, n, r) { + return _(e, t, n, r) + } + + function E(e, t, n, r) { + return B(z(t), e, n, r) + } + + function x(e, t, n, r) { + return B(function(e, t) { + for (var n, r, o, i = [], a = 0; a < e.length && !((t -= 2) < 0); ++a) n = e.charCodeAt(a), r = n >> 8, o = n % 256, i.push(o), i.push(r); + return i + }(t, e.length - n), e, n, r) + } + + function S(e, t, n) { + return 0 === t && n === e.length ? r.fromByteArray(e) : r.fromByteArray(e.slice(t, n)) + } + + function C(e, t, n) { + n = Math.min(e.length, n); + for (var r = [], o = t; o < n;) { + var i, a, u, s, l = e[o], + c = null, + f = l > 239 ? 4 : l > 223 ? 3 : l > 191 ? 2 : 1; + if (o + f <= n) switch (f) { + case 1: + l < 128 && (c = l); + break; + case 2: + 128 == (192 & (i = e[o + 1])) && (s = (31 & l) << 6 | 63 & i) > 127 && (c = s); + break; + case 3: + i = e[o + 1], a = e[o + 2], 128 == (192 & i) && 128 == (192 & a) && (s = (15 & l) << 12 | (63 & i) << 6 | 63 & a) > 2047 && (s < 55296 || s > 57343) && (c = s); + break; + case 4: + i = e[o + 1], a = e[o + 2], u = e[o + 3], 128 == (192 & i) && 128 == (192 & a) && 128 == (192 & u) && (s = (15 & l) << 18 | (63 & i) << 12 | (63 & a) << 6 | 63 & u) > 65535 && s < 1114112 && (c = s) + } + null === c ? (c = 65533, f = 1) : c > 65535 && (c -= 65536, r.push(c >>> 10 & 1023 | 55296), c = 56320 | 1023 & c), r.push(c), o += f + } + return function(e) { + var t = e.length; + if (t <= k) return String.fromCharCode.apply(String, e); + var n = "", + r = 0; + for (; r < t;) n += String.fromCharCode.apply(String, e.slice(r, r += k)); + return n + }(r) + } + t.Buffer = s, t.SlowBuffer = function(e) { + +e != e && (e = 0); + return s.alloc(+e) + }, t.INSPECT_MAX_BYTES = 50, s.TYPED_ARRAY_SUPPORT = void 0 !== e.TYPED_ARRAY_SUPPORT ? e.TYPED_ARRAY_SUPPORT : function() { + try { + var e = new Uint8Array(1); + return e.__proto__ = { + __proto__: Uint8Array.prototype, + foo: function() { + return 42 + } + }, 42 === e.foo() && "function" == typeof e.subarray && 0 === e.subarray(1, 1).byteLength + } catch (e) { + return !1 + } + }(), t.kMaxLength = a(), s.poolSize = 8192, s._augment = function(e) { + return e.__proto__ = s.prototype, e + }, s.from = function(e, t, n) { + return l(null, e, t, n) + }, s.TYPED_ARRAY_SUPPORT && (s.prototype.__proto__ = Uint8Array.prototype, s.__proto__ = Uint8Array, "undefined" != typeof Symbol && Symbol.species && s[Symbol.species] === s && Object.defineProperty(s, Symbol.species, { + value: null, + configurable: !0 + })), s.alloc = function(e, t, n) { + return function(e, t, n, r) { + return c(t), t <= 0 ? u(e, t) : void 0 !== n ? "string" == typeof r ? u(e, t).fill(n, r) : u(e, t).fill(n) : u(e, t) + }(null, e, t, n) + }, s.allocUnsafe = function(e) { + return f(null, e) + }, s.allocUnsafeSlow = function(e) { + return f(null, e) + }, s.isBuffer = function(e) { + return !(null == e || !e._isBuffer) + }, s.compare = function(e, t) { + if (!s.isBuffer(e) || !s.isBuffer(t)) throw new TypeError("Arguments must be Buffers"); + if (e === t) return 0; + for (var n = e.length, r = t.length, o = 0, i = Math.min(n, r); o < i; ++o) + if (e[o] !== t[o]) { + n = e[o], r = t[o]; + break + } + return n < r ? -1 : r < n ? 1 : 0 + }, s.isEncoding = function(e) { + switch (String(e).toLowerCase()) { + case "hex": + case "utf8": + case "utf-8": + case "ascii": + case "latin1": + case "binary": + case "base64": + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return !0; + default: + return !1 + } + }, s.concat = function(e, t) { + if (!i(e)) throw new TypeError('"list" argument must be an Array of Buffers'); + if (0 === e.length) return s.alloc(0); + var n; + if (void 0 === t) + for (t = 0, n = 0; n < e.length; ++n) t += e[n].length; + var r = s.allocUnsafe(t), + o = 0; + for (n = 0; n < e.length; ++n) { + var a = e[n]; + if (!s.isBuffer(a)) throw new TypeError('"list" argument must be an Array of Buffers'); + a.copy(r, o), o += a.length + } + return r + }, s.byteLength = h, s.prototype._isBuffer = !0, s.prototype.swap16 = function() { + var e = this.length; + if (e % 2 != 0) throw new RangeError("Buffer size must be a multiple of 16-bits"); + for (var t = 0; t < e; t += 2) v(this, t, t + 1); + return this + }, s.prototype.swap32 = function() { + var e = this.length; + if (e % 4 != 0) throw new RangeError("Buffer size must be a multiple of 32-bits"); + for (var t = 0; t < e; t += 4) v(this, t, t + 3), v(this, t + 1, t + 2); + return this + }, s.prototype.swap64 = function() { + var e = this.length; + if (e % 8 != 0) throw new RangeError("Buffer size must be a multiple of 64-bits"); + for (var t = 0; t < e; t += 8) v(this, t, t + 7), v(this, t + 1, t + 6), v(this, t + 2, t + 5), v(this, t + 3, t + 4); + return this + }, s.prototype.toString = function() { + var e = 0 | this.length; + return 0 === e ? "" : 0 === arguments.length ? C(this, 0, e) : function(e, t, n) { + var r = !1; + if ((void 0 === t || t < 0) && (t = 0), t > this.length) return ""; + if ((void 0 === n || n > this.length) && (n = this.length), n <= 0) return ""; + if ((n >>>= 0) <= (t >>>= 0)) return ""; + for (e || (e = "utf8");;) switch (e) { + case "hex": + return P(this, t, n); + case "utf8": + case "utf-8": + return C(this, t, n); + case "ascii": + return A(this, t, n); + case "latin1": + case "binary": + return O(this, t, n); + case "base64": + return S(this, t, n); + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return T(this, t, n); + default: + if (r) throw new TypeError("Unknown encoding: " + e); + e = (e + "").toLowerCase(), r = !0 + } + }.apply(this, arguments) + }, s.prototype.equals = function(e) { + if (!s.isBuffer(e)) throw new TypeError("Argument must be a Buffer"); + return this === e || 0 === s.compare(this, e) + }, s.prototype.inspect = function() { + var e = "", + n = t.INSPECT_MAX_BYTES; + return this.length > 0 && (e = this.toString("hex", 0, n).match(/.{2}/g).join(" "), this.length > n && (e += " ... ")), "" + }, s.prototype.compare = function(e, t, n, r, o) { + if (!s.isBuffer(e)) throw new TypeError("Argument must be a Buffer"); + if (void 0 === t && (t = 0), void 0 === n && (n = e ? e.length : 0), void 0 === r && (r = 0), void 0 === o && (o = this.length), t < 0 || n > e.length || r < 0 || o > this.length) throw new RangeError("out of range index"); + if (r >= o && t >= n) return 0; + if (r >= o) return -1; + if (t >= n) return 1; + if (t >>>= 0, n >>>= 0, r >>>= 0, o >>>= 0, this === e) return 0; + for (var i = o - r, a = n - t, u = Math.min(i, a), l = this.slice(r, o), c = e.slice(t, n), f = 0; f < u; ++f) + if (l[f] !== c[f]) { + i = l[f], a = c[f]; + break + } + return i < a ? -1 : a < i ? 1 : 0 + }, s.prototype.includes = function(e, t, n) { + return -1 !== this.indexOf(e, t, n) + }, s.prototype.indexOf = function(e, t, n) { + return m(this, e, t, n, !0) + }, s.prototype.lastIndexOf = function(e, t, n) { + return m(this, e, t, n, !1) + }, s.prototype.write = function(e, t, n, r) { + if (void 0 === t) r = "utf8", n = this.length, t = 0; + else if (void 0 === n && "string" == typeof t) r = t, n = this.length, t = 0; + else { + if (!isFinite(t)) throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported"); + t |= 0, isFinite(n) ? (n |= 0, void 0 === r && (r = "utf8")) : (r = n, n = void 0) + } + var o = this.length - t; + if ((void 0 === n || n > o) && (n = o), e.length > 0 && (n < 0 || t < 0) || t > this.length) throw new RangeError("Attempt to write outside buffer bounds"); + r || (r = "utf8"); + for (var i = !1;;) switch (r) { + case "hex": + return y(this, e, t, n); + case "utf8": + case "utf-8": + return b(this, e, t, n); + case "ascii": + return _(this, e, t, n); + case "latin1": + case "binary": + return w(this, e, t, n); + case "base64": + return E(this, e, t, n); + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return x(this, e, t, n); + default: + if (i) throw new TypeError("Unknown encoding: " + r); + r = ("" + r).toLowerCase(), i = !0 + } + }, s.prototype.toJSON = function() { + return { + type: "Buffer", + data: Array.prototype.slice.call(this._arr || this, 0) + } + }; + var k = 4096; + + function A(e, t, n) { + var r = ""; + n = Math.min(e.length, n); + for (var o = t; o < n; ++o) r += String.fromCharCode(127 & e[o]); + return r + } + + function O(e, t, n) { + var r = ""; + n = Math.min(e.length, n); + for (var o = t; o < n; ++o) r += String.fromCharCode(e[o]); + return r + } + + function P(e, t, n) { + var r = e.length; + (!t || t < 0) && (t = 0), (!n || n < 0 || n > r) && (n = r); + for (var o = "", i = t; i < n; ++i) o += q(e[i]); + return o + } + + function T(e, t, n) { + for (var r = e.slice(t, n), o = "", i = 0; i < r.length; i += 2) o += String.fromCharCode(r[i] + 256 * r[i + 1]); + return o + } + + function M(e, t, n) { + if (e % 1 != 0 || e < 0) throw new RangeError("offset is not uint"); + if (e + t > n) throw new RangeError("Trying to access beyond buffer length") + } + + function I(e, t, n, r, o, i) { + if (!s.isBuffer(e)) throw new TypeError('"buffer" argument must be a Buffer instance'); + if (t > o || t < i) throw new RangeError('"value" argument is out of bounds'); + if (n + r > e.length) throw new RangeError("Index out of range") + } + + function j(e, t, n, r) { + t < 0 && (t = 65535 + t + 1); + for (var o = 0, i = Math.min(e.length - n, 2); o < i; ++o) e[n + o] = (t & 255 << 8 * (r ? o : 1 - o)) >>> 8 * (r ? o : 1 - o) + } + + function N(e, t, n, r) { + t < 0 && (t = 4294967295 + t + 1); + for (var o = 0, i = Math.min(e.length - n, 4); o < i; ++o) e[n + o] = t >>> 8 * (r ? o : 3 - o) & 255 + } + + function R(e, t, n, r, o, i) { + if (n + r > e.length) throw new RangeError("Index out of range"); + if (n < 0) throw new RangeError("Index out of range") + } + + function D(e, t, n, r, i) { + return i || R(e, 0, n, 4), o.write(e, t, n, r, 23, 4), n + 4 + } + + function L(e, t, n, r, i) { + return i || R(e, 0, n, 8), o.write(e, t, n, r, 52, 8), n + 8 + } + s.prototype.slice = function(e, t) { + var n, r = this.length; + if (e = ~~e, t = void 0 === t ? r : ~~t, e < 0 ? (e += r) < 0 && (e = 0) : e > r && (e = r), t < 0 ? (t += r) < 0 && (t = 0) : t > r && (t = r), t < e && (t = e), s.TYPED_ARRAY_SUPPORT)(n = this.subarray(e, t)).__proto__ = s.prototype; + else { + var o = t - e; + n = new s(o, void 0); + for (var i = 0; i < o; ++i) n[i] = this[i + e] + } + return n + }, s.prototype.readUIntLE = function(e, t, n) { + e |= 0, t |= 0, n || M(e, t, this.length); + for (var r = this[e], o = 1, i = 0; ++i < t && (o *= 256);) r += this[e + i] * o; + return r + }, s.prototype.readUIntBE = function(e, t, n) { + e |= 0, t |= 0, n || M(e, t, this.length); + for (var r = this[e + --t], o = 1; t > 0 && (o *= 256);) r += this[e + --t] * o; + return r + }, s.prototype.readUInt8 = function(e, t) { + return t || M(e, 1, this.length), this[e] + }, s.prototype.readUInt16LE = function(e, t) { + return t || M(e, 2, this.length), this[e] | this[e + 1] << 8 + }, s.prototype.readUInt16BE = function(e, t) { + return t || M(e, 2, this.length), this[e] << 8 | this[e + 1] + }, s.prototype.readUInt32LE = function(e, t) { + return t || M(e, 4, this.length), (this[e] | this[e + 1] << 8 | this[e + 2] << 16) + 16777216 * this[e + 3] + }, s.prototype.readUInt32BE = function(e, t) { + return t || M(e, 4, this.length), 16777216 * this[e] + (this[e + 1] << 16 | this[e + 2] << 8 | this[e + 3]) + }, s.prototype.readIntLE = function(e, t, n) { + e |= 0, t |= 0, n || M(e, t, this.length); + for (var r = this[e], o = 1, i = 0; ++i < t && (o *= 256);) r += this[e + i] * o; + return r >= (o *= 128) && (r -= Math.pow(2, 8 * t)), r + }, s.prototype.readIntBE = function(e, t, n) { + e |= 0, t |= 0, n || M(e, t, this.length); + for (var r = t, o = 1, i = this[e + --r]; r > 0 && (o *= 256);) i += this[e + --r] * o; + return i >= (o *= 128) && (i -= Math.pow(2, 8 * t)), i + }, s.prototype.readInt8 = function(e, t) { + return t || M(e, 1, this.length), 128 & this[e] ? -1 * (255 - this[e] + 1) : this[e] + }, s.prototype.readInt16LE = function(e, t) { + t || M(e, 2, this.length); + var n = this[e] | this[e + 1] << 8; + return 32768 & n ? 4294901760 | n : n + }, s.prototype.readInt16BE = function(e, t) { + t || M(e, 2, this.length); + var n = this[e + 1] | this[e] << 8; + return 32768 & n ? 4294901760 | n : n + }, s.prototype.readInt32LE = function(e, t) { + return t || M(e, 4, this.length), this[e] | this[e + 1] << 8 | this[e + 2] << 16 | this[e + 3] << 24 + }, s.prototype.readInt32BE = function(e, t) { + return t || M(e, 4, this.length), this[e] << 24 | this[e + 1] << 16 | this[e + 2] << 8 | this[e + 3] + }, s.prototype.readFloatLE = function(e, t) { + return t || M(e, 4, this.length), o.read(this, e, !0, 23, 4) + }, s.prototype.readFloatBE = function(e, t) { + return t || M(e, 4, this.length), o.read(this, e, !1, 23, 4) + }, s.prototype.readDoubleLE = function(e, t) { + return t || M(e, 8, this.length), o.read(this, e, !0, 52, 8) + }, s.prototype.readDoubleBE = function(e, t) { + return t || M(e, 8, this.length), o.read(this, e, !1, 52, 8) + }, s.prototype.writeUIntLE = function(e, t, n, r) { + (e = +e, t |= 0, n |= 0, r) || I(this, e, t, n, Math.pow(2, 8 * n) - 1, 0); + var o = 1, + i = 0; + for (this[t] = 255 & e; ++i < n && (o *= 256);) this[t + i] = e / o & 255; + return t + n + }, s.prototype.writeUIntBE = function(e, t, n, r) { + (e = +e, t |= 0, n |= 0, r) || I(this, e, t, n, Math.pow(2, 8 * n) - 1, 0); + var o = n - 1, + i = 1; + for (this[t + o] = 255 & e; --o >= 0 && (i *= 256);) this[t + o] = e / i & 255; + return t + n + }, s.prototype.writeUInt8 = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 1, 255, 0), s.TYPED_ARRAY_SUPPORT || (e = Math.floor(e)), this[t] = 255 & e, t + 1 + }, s.prototype.writeUInt16LE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 2, 65535, 0), s.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8) : j(this, e, t, !0), t + 2 + }, s.prototype.writeUInt16BE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 2, 65535, 0), s.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 8, this[t + 1] = 255 & e) : j(this, e, t, !1), t + 2 + }, s.prototype.writeUInt32LE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 4, 4294967295, 0), s.TYPED_ARRAY_SUPPORT ? (this[t + 3] = e >>> 24, this[t + 2] = e >>> 16, this[t + 1] = e >>> 8, this[t] = 255 & e) : N(this, e, t, !0), t + 4 + }, s.prototype.writeUInt32BE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 4, 4294967295, 0), s.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 24, this[t + 1] = e >>> 16, this[t + 2] = e >>> 8, this[t + 3] = 255 & e) : N(this, e, t, !1), t + 4 + }, s.prototype.writeIntLE = function(e, t, n, r) { + if (e = +e, t |= 0, !r) { + var o = Math.pow(2, 8 * n - 1); + I(this, e, t, n, o - 1, -o) + } + var i = 0, + a = 1, + u = 0; + for (this[t] = 255 & e; ++i < n && (a *= 256);) e < 0 && 0 === u && 0 !== this[t + i - 1] && (u = 1), this[t + i] = (e / a >> 0) - u & 255; + return t + n + }, s.prototype.writeIntBE = function(e, t, n, r) { + if (e = +e, t |= 0, !r) { + var o = Math.pow(2, 8 * n - 1); + I(this, e, t, n, o - 1, -o) + } + var i = n - 1, + a = 1, + u = 0; + for (this[t + i] = 255 & e; --i >= 0 && (a *= 256);) e < 0 && 0 === u && 0 !== this[t + i + 1] && (u = 1), this[t + i] = (e / a >> 0) - u & 255; + return t + n + }, s.prototype.writeInt8 = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 1, 127, -128), s.TYPED_ARRAY_SUPPORT || (e = Math.floor(e)), e < 0 && (e = 255 + e + 1), this[t] = 255 & e, t + 1 + }, s.prototype.writeInt16LE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 2, 32767, -32768), s.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8) : j(this, e, t, !0), t + 2 + }, s.prototype.writeInt16BE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 2, 32767, -32768), s.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 8, this[t + 1] = 255 & e) : j(this, e, t, !1), t + 2 + }, s.prototype.writeInt32LE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 4, 2147483647, -2147483648), s.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8, this[t + 2] = e >>> 16, this[t + 3] = e >>> 24) : N(this, e, t, !0), t + 4 + }, s.prototype.writeInt32BE = function(e, t, n) { + return e = +e, t |= 0, n || I(this, e, t, 4, 2147483647, -2147483648), e < 0 && (e = 4294967295 + e + 1), s.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 24, this[t + 1] = e >>> 16, this[t + 2] = e >>> 8, this[t + 3] = 255 & e) : N(this, e, t, !1), t + 4 + }, s.prototype.writeFloatLE = function(e, t, n) { + return D(this, e, t, !0, n) + }, s.prototype.writeFloatBE = function(e, t, n) { + return D(this, e, t, !1, n) + }, s.prototype.writeDoubleLE = function(e, t, n) { + return L(this, e, t, !0, n) + }, s.prototype.writeDoubleBE = function(e, t, n) { + return L(this, e, t, !1, n) + }, s.prototype.copy = function(e, t, n, r) { + if (n || (n = 0), r || 0 === r || (r = this.length), t >= e.length && (t = e.length), t || (t = 0), r > 0 && r < n && (r = n), r === n) return 0; + if (0 === e.length || 0 === this.length) return 0; + if (t < 0) throw new RangeError("targetStart out of bounds"); + if (n < 0 || n >= this.length) throw new RangeError("sourceStart out of bounds"); + if (r < 0) throw new RangeError("sourceEnd out of bounds"); + r > this.length && (r = this.length), e.length - t < r - n && (r = e.length - t + n); + var o, i = r - n; + if (this === e && n < t && t < r) + for (o = i - 1; o >= 0; --o) e[o + t] = this[o + n]; + else if (i < 1e3 || !s.TYPED_ARRAY_SUPPORT) + for (o = 0; o < i; ++o) e[o + t] = this[o + n]; + else Uint8Array.prototype.set.call(e, this.subarray(n, n + i), t); + return i + }, s.prototype.fill = function(e, t, n, r) { + if ("string" == typeof e) { + if ("string" == typeof t ? (r = t, t = 0, n = this.length) : "string" == typeof n && (r = n, n = this.length), 1 === e.length) { + var o = e.charCodeAt(0); + o < 256 && (e = o) + } + if (void 0 !== r && "string" != typeof r) throw new TypeError("encoding must be a string"); + if ("string" == typeof r && !s.isEncoding(r)) throw new TypeError("Unknown encoding: " + r) + } else "number" == typeof e && (e &= 255); + if (t < 0 || this.length < t || this.length < n) throw new RangeError("Out of range index"); + if (n <= t) return this; + var i; + if (t >>>= 0, n = void 0 === n ? this.length : n >>> 0, e || (e = 0), "number" == typeof e) + for (i = t; i < n; ++i) this[i] = e; + else { + var a = s.isBuffer(e) ? e : F(new s(e, r).toString()), + u = a.length; + for (i = 0; i < n - t; ++i) this[i + t] = a[i % u] + } + return this + }; + var U = /[^+\/0-9A-Za-z-_]/g; + + function q(e) { + return e < 16 ? "0" + e.toString(16) : e.toString(16) + } + + function F(e, t) { + var n; + t = t || 1 / 0; + for (var r = e.length, o = null, i = [], a = 0; a < r; ++a) { + if ((n = e.charCodeAt(a)) > 55295 && n < 57344) { + if (!o) { + if (n > 56319) { + (t -= 3) > -1 && i.push(239, 191, 189); + continue + } + if (a + 1 === r) { + (t -= 3) > -1 && i.push(239, 191, 189); + continue + } + o = n; + continue + } + if (n < 56320) { + (t -= 3) > -1 && i.push(239, 191, 189), o = n; + continue + } + n = 65536 + (o - 55296 << 10 | n - 56320) + } else o && (t -= 3) > -1 && i.push(239, 191, 189); + if (o = null, n < 128) { + if ((t -= 1) < 0) break; + i.push(n) + } else if (n < 2048) { + if ((t -= 2) < 0) break; + i.push(n >> 6 | 192, 63 & n | 128) + } else if (n < 65536) { + if ((t -= 3) < 0) break; + i.push(n >> 12 | 224, n >> 6 & 63 | 128, 63 & n | 128) + } else { + if (!(n < 1114112)) throw new Error("Invalid code point"); + if ((t -= 4) < 0) break; + i.push(n >> 18 | 240, n >> 12 & 63 | 128, n >> 6 & 63 | 128, 63 & n | 128) + } + } + return i + } + + function z(e) { + return r.toByteArray(function(e) { + if ((e = function(e) { + return e.trim ? e.trim() : e.replace(/^\s+|\s+$/g, "") + }(e).replace(U, "")).length < 2) return ""; + for (; e.length % 4 != 0;) e += "="; + return e + }(e)) + } + + function B(e, t, n, r) { + for (var o = 0; o < r && !(o + n >= t.length || o >= e.length); ++o) t[o + n] = e[o]; + return o + } + }).call(t, n(31)) + }, function(e, t) { + var n, r, o = e.exports = {}; + + function i() { + throw new Error("setTimeout has not been defined") + } + + function a() { + throw new Error("clearTimeout has not been defined") + } + + function u(e) { + if (n === setTimeout) return setTimeout(e, 0); + if ((n === i || !n) && setTimeout) return n = setTimeout, setTimeout(e, 0); + try { + return n(e, 0) + } catch (t) { + try { + return n.call(null, e, 0) + } catch (t) { + return n.call(this, e, 0) + } + } + }! function() { + try { + n = "function" == typeof setTimeout ? setTimeout : i + } catch (e) { + n = i + } + try { + r = "function" == typeof clearTimeout ? clearTimeout : a + } catch (e) { + r = a + } + }(); + var s, l = [], + c = !1, + f = -1; + + function p() { + c && s && (c = !1, s.length ? l = s.concat(l) : f = -1, l.length && d()) + } + + function d() { + if (!c) { + var e = u(p); + c = !0; + for (var t = l.length; t;) { + for (s = l, l = []; ++f < t;) s && s[f].run(); + f = -1, t = l.length + } + s = null, c = !1, + function(e) { + if (r === clearTimeout) return clearTimeout(e); + if ((r === a || !r) && clearTimeout) return r = clearTimeout, clearTimeout(e); + try { + r(e) + } catch (t) { + try { + return r.call(null, e) + } catch (t) { + return r.call(this, e) + } + } + }(e) + } + } + + function h(e, t) { + this.fun = e, this.array = t + } + + function v() {} + o.nextTick = function(e) { + var t = new Array(arguments.length - 1); + if (arguments.length > 1) + for (var n = 1; n < arguments.length; n++) t[n - 1] = arguments[n]; + l.push(new h(e, t)), 1 !== l.length || c || u(d) + }, h.prototype.run = function() { + this.fun.apply(null, this.array) + }, o.title = "browser", o.browser = !0, o.env = {}, o.argv = [], o.version = "", o.versions = {}, o.on = v, o.addListener = v, o.once = v, o.off = v, o.removeListener = v, o.removeAllListeners = v, o.emit = v, o.prependListener = v, o.prependOnceListener = v, o.listeners = function(e) { + return [] + }, o.binding = function(e) { + throw new Error("process.binding is not supported") + }, o.cwd = function() { + return "/" + }, o.chdir = function(e) { + throw new Error("process.chdir is not supported") + }, o.umask = function() { + return 0 + } + }, function(e, t, n) { + "use strict"; + e.exports = function(e) { + if ("function" != typeof e) throw new TypeError(e + " is not a function"); + return e + } + }, function(e, t, n) { + "use strict"; + + function r(e, t) { + return e === t + } + + function o(e) { + var t = arguments.length <= 1 || void 0 === arguments[1] ? r : arguments[1], + n = null, + o = null; + return function() { + for (var r = arguments.length, i = Array(r), a = 0; a < r; a++) i[a] = arguments[a]; + return null !== n && n.length === i.length && i.every(function(e, r) { + return t(e, n[r]) + }) || (o = e.apply(void 0, i)), n = i, o + } + } + + function i(e) { + for (var t = arguments.length, n = Array(t > 1 ? t - 1 : 0), r = 1; r < t; r++) n[r - 1] = arguments[r]; + return function() { + for (var t = arguments.length, r = Array(t), o = 0; o < t; o++) r[o] = arguments[o]; + var i = 0, + a = r.pop(), + u = function(e) { + var t = Array.isArray(e[0]) ? e[0] : e; + if (!t.every(function(e) { + return "function" == typeof e + })) { + var n = t.map(function(e) { + return typeof e + }).join(", "); + throw new Error("Selector creators expect all input-selectors to be functions, instead received the following types: [" + n + "]") + } + return t + }(r), + s = e.apply(void 0, [function() { + return i++, a.apply(void 0, arguments) + }].concat(n)), + l = function(e, t) { + for (var n = arguments.length, r = Array(n > 2 ? n - 2 : 0), o = 2; o < n; o++) r[o - 2] = arguments[o]; + var i = u.map(function(n) { + return n.apply(void 0, [e, t].concat(r)) + }); + return s.apply(void 0, function(e) { + if (Array.isArray(e)) { + for (var t = 0, n = Array(e.length); t < e.length; t++) n[t] = e[t]; + return n + } + return Array.from(e) + }(i)) + }; + return l.resultFunc = a, l.recomputations = function() { + return i + }, l.resetRecomputations = function() { + return i = 0 + }, l + } + } + t.__esModule = !0, t.defaultMemoize = o, t.createSelectorCreator = i, t.createStructuredSelector = function(e) { + var t = arguments.length <= 1 || void 0 === arguments[1] ? a : arguments[1]; + if ("object" != typeof e) throw new Error("createStructuredSelector expects first argument to be an object where each property is a selector, instead received a " + typeof e); + var n = Object.keys(e); + return t(n.map(function(t) { + return e[t] + }), function() { + for (var e = arguments.length, t = Array(e), r = 0; r < e; r++) t[r] = arguments[r]; + return t.reduce(function(e, t, r) { + return e[n[r]] = t, e + }, {}) + }) + }; + var a = t.createSelector = i(o) + }, function(e, t, n) { + var r = n(117), + o = n(243); + e.exports = n(100) ? function(e, t, n) { + return r.f(e, t, o(1, n)) + } : function(e, t, n) { + return e[t] = n, e + } + }, function(e, t, n) { + var r = n(74); + e.exports = function(e) { + if (!r(e)) throw TypeError(e + " is not an object!"); + return e + } + }, function(e, t) { + var n = e.exports = { + version: "2.5.5" + }; + "number" == typeof __e && (__e = n) + }, function(e, t, n) { + var r = n(277); + e.exports = function(e) { + return null == e ? "" : r(e) + } + }, function(e, t, n) { + var r = n(77), + o = n(574), + i = n(575), + a = "[object Null]", + u = "[object Undefined]", + s = r ? r.toStringTag : void 0; + e.exports = function(e) { + return null == e ? void 0 === e ? u : a : s && s in Object(e) ? o(e) : i(e) + } + }, function(e, t, n) { + var r = n(592), + o = n(595); + e.exports = function(e, t) { + var n = o(e, t); + return r(n) ? n : void 0 + } + }, function(e, t, n) { + var r = n(295), + o = n(632), + i = n(78); + e.exports = function(e) { + return i(e) ? r(e) : o(e) + } + }, function(e, t, n) { + "use strict"; + var r = n(140), + o = Object.keys || function(e) { + var t = []; + for (var n in e) t.push(n); + return t + }; + e.exports = f; + var i = n(106); + i.inherits = n(81); + var a = n(305), + u = n(195); + i.inherits(f, a); + for (var s = o(u.prototype), l = 0; l < s.length; l++) { + var c = s[l]; + f.prototype[c] || (f.prototype[c] = u.prototype[c]) + } + + function f(e) { + if (!(this instanceof f)) return new f(e); + a.call(this, e), u.call(this, e), e && !1 === e.readable && (this.readable = !1), e && !1 === e.writable && (this.writable = !1), this.allowHalfOpen = !0, e && !1 === e.allowHalfOpen && (this.allowHalfOpen = !1), this.once("end", p) + } + + function p() { + this.allowHalfOpen || this._writableState.ended || r.nextTick(d, this) + } + + function d(e) { + e.end() + } + Object.defineProperty(f.prototype, "writableHighWaterMark", { + enumerable: !1, + get: function() { + return this._writableState.highWaterMark + } + }), Object.defineProperty(f.prototype, "destroyed", { + get: function() { + return void 0 !== this._readableState && void 0 !== this._writableState && (this._readableState.destroyed && this._writableState.destroyed) + }, + set: function(e) { + void 0 !== this._readableState && void 0 !== this._writableState && (this._readableState.destroyed = e, this._writableState.destroyed = e) + } + }), f.prototype._destroy = function(e, t) { + this.push(null), this.end(), r.nextTick(t, e) + } + }, function(e, t, n) { + "use strict"; + var r = n(312)(); + e.exports = function(e) { + return e !== r && null !== e + } + }, function(e, t, n) { + "use strict"; + var r = n(670), + o = Math.max; + e.exports = function(e) { + return o(0, r(e)) + } + }, function(e, t, n) { + "use strict" + }, function(e, t, n) { + "use strict"; + var r = n(11), + o = (n(8), function(e) { + if (this.instancePool.length) { + var t = this.instancePool.pop(); + return this.call(t, e), t + } + return new this(e) + }), + i = function(e) { + e instanceof this || r("25"), e.destructor(), this.instancePool.length < this.poolSize && this.instancePool.push(e) + }, + a = o, + u = { + addPoolingTo: function(e, t) { + var n = e; + return n.instancePool = [], n.getPooled = t || a, n.poolSize || (n.poolSize = 10), n.release = i, n + }, + oneArgumentPooler: o, + twoArgumentPooler: function(e, t) { + if (this.instancePool.length) { + var n = this.instancePool.pop(); + return this.call(n, e, t), n + } + return new this(e, t) + }, + threeArgumentPooler: function(e, t, n) { + if (this.instancePool.length) { + var r = this.instancePool.pop(); + return this.call(r, e, t, n), r + } + return new this(e, t, n) + }, + fourArgumentPooler: function(e, t, n, r) { + if (this.instancePool.length) { + var o = this.instancePool.pop(); + return this.call(o, e, t, n, r), o + } + return new this(e, t, n, r) + } + }; + e.exports = u + }, function(e, t) { + e.exports = {} + }, function(e, t, n) { + var r = n(154), + o = n(155); + e.exports = function(e) { + return r(o(e)) + } + }, function(e, t, n) { + var r = n(155); + e.exports = function(e) { + return Object(r(e)) + } + }, function(e, t, n) { + var r = n(33), + o = n(58), + i = n(118), + a = n(167)("src"), + u = Function.toString, + s = ("" + u).split("toString"); + n(60).inspectSource = function(e) { + return u.call(e) + }, (e.exports = function(e, t, n, u) { + var l = "function" == typeof n; + l && (i(n, "name") || o(n, "name", t)), e[t] !== n && (l && (i(n, a) || o(n, a, e[t] ? "" + e[t] : s.join(String(t)))), e === r ? e[t] = n : u ? e[t] ? e[t] = n : o(e, t, n) : (delete e[t], o(e, t, n))) + })(Function.prototype, "toString", function() { + return "function" == typeof this && this[a] || u.call(this) + }) + }, function(e, t) { + e.exports = function(e) { + return "object" == typeof e ? null !== e : "function" == typeof e + } + }, function(e, t, n) { + "use strict"; + var r = n(13), + o = n(264), + i = n(536), + a = n(541), + u = n(76), + s = n(542), + l = n(545), + c = n(546), + f = n(548), + p = u.createElement, + d = u.createFactory, + h = u.cloneElement, + v = r, + m = function(e) { + return e + }, + g = { + Children: { + map: i.map, + forEach: i.forEach, + count: i.count, + toArray: i.toArray, + only: f + }, + Component: o.Component, + PureComponent: o.PureComponent, + createElement: p, + cloneElement: h, + isValidElement: u.isValidElement, + PropTypes: s, + createClass: c, + createFactory: d, + createMixin: m, + DOM: a, + version: l, + __spread: v + }; + e.exports = g + }, function(e, t, n) { + "use strict"; + var r = n(13), + o = n(46), + i = (n(9), n(266), Object.prototype.hasOwnProperty), + a = n(267), + u = { + key: !0, + ref: !0, + __self: !0, + __source: !0 + }; + + function s(e) { + return void 0 !== e.ref + } + + function l(e) { + return void 0 !== e.key + } + var c = function(e, t, n, r, o, i, u) { + var s = { + $$typeof: a, + type: e, + key: t, + ref: n, + props: u, + _owner: i + }; + return s + }; + c.createElement = function(e, t, n) { + var r, a = {}, + f = null, + p = null; + if (null != t) + for (r in s(t) && (p = t.ref), l(t) && (f = "" + t.key), void 0 === t.__self ? null : t.__self, void 0 === t.__source ? null : t.__source, t) i.call(t, r) && !u.hasOwnProperty(r) && (a[r] = t[r]); + var d = arguments.length - 2; + if (1 === d) a.children = n; + else if (d > 1) { + for (var h = Array(d), v = 0; v < d; v++) h[v] = arguments[v + 2]; + 0, a.children = h + } + if (e && e.defaultProps) { + var m = e.defaultProps; + for (r in m) void 0 === a[r] && (a[r] = m[r]) + } + return c(e, f, p, 0, 0, o.current, a) + }, c.createFactory = function(e) { + var t = c.createElement.bind(null, e); + return t.type = e, t + }, c.cloneAndReplaceKey = function(e, t) { + return c(e.type, t, e.ref, e._self, e._source, e._owner, e.props) + }, c.cloneElement = function(e, t, n) { + var a, f, p = r({}, e.props), + d = e.key, + h = e.ref, + v = (e._self, e._source, e._owner); + if (null != t) + for (a in s(t) && (h = t.ref, v = o.current), l(t) && (d = "" + t.key), e.type && e.type.defaultProps && (f = e.type.defaultProps), t) i.call(t, a) && !u.hasOwnProperty(a) && (void 0 === t[a] && void 0 !== f ? p[a] = f[a] : p[a] = t[a]); + var m = arguments.length - 2; + if (1 === m) p.children = n; + else if (m > 1) { + for (var g = Array(m), y = 0; y < m; y++) g[y] = arguments[y + 2]; + p.children = g + } + return c(e.type, d, h, 0, 0, v, p) + }, c.isValidElement = function(e) { + return "object" == typeof e && null !== e && e.$$typeof === a + }, e.exports = c + }, function(e, t, n) { + var r = n(37).Symbol; + e.exports = r + }, function(e, t, n) { + var r = n(285), + o = n(187); + e.exports = function(e) { + return null != e && o(e.length) && !r(e) + } + }, function(e, t, n) { + var r = n(24), + o = n(190), + i = n(640), + a = n(61); + e.exports = function(e, t) { + return r(e) ? e : o(e, t) ? [e] : i(a(e)) + } + }, function(e, t, n) { + var r = n(128), + o = 1 / 0; + e.exports = function(e) { + if ("string" == typeof e || r(e)) return e; + var t = e + ""; + return "0" == t && 1 / e == -o ? "-0" : t + } + }, function(e, t) { + "function" == typeof Object.create ? e.exports = function(e, t) { + e.super_ = t, e.prototype = Object.create(t.prototype, { + constructor: { + value: e, + enumerable: !1, + writable: !0, + configurable: !0 + } + }) + } : e.exports = function(e, t) { + e.super_ = t; + var n = function() {}; + n.prototype = t.prototype, e.prototype = new n, e.prototype.constructor = e + } + }, function(e, t, n) { + "use strict"; + var r = n(66); + e.exports = function(e) { + if (!r(e)) throw new TypeError("Cannot use null or undefined"); + return e + } + }, function(e, t, n) { + "use strict"; + t.__esModule = !0; + var r, o = n(728), + i = (r = o) && r.__esModule ? r : { + default: r + }; + t.default = function(e) { + if (Array.isArray(e)) { + for (var t = 0, n = Array(e.length); t < e.length; t++) n[t] = e[t]; + return n + } + return (0, i.default)(e) + } + }, function(e, t, n) { + "use strict"; + t.__esModule = !0, t.default = function(e, t) { + var n = {}; + for (var r in e) t.indexOf(r) >= 0 || Object.prototype.hasOwnProperty.call(e, r) && (n[r] = e[r]); + return n + } + }, function(e, t, n) { + "use strict"; + + function r(e) { + return void 0 === e || null === e + } + e.exports.isNothing = r, e.exports.isObject = function(e) { + return "object" == typeof e && null !== e + }, e.exports.toArray = function(e) { + return Array.isArray(e) ? e : r(e) ? [] : [e] + }, e.exports.repeat = function(e, t) { + var n, r = ""; + for (n = 0; n < t; n += 1) r += e; + return r + }, e.exports.isNegativeZero = function(e) { + return 0 === e && Number.NEGATIVE_INFINITY === 1 / e + }, e.exports.extend = function(e, t) { + var n, r, o, i; + if (t) + for (n = 0, r = (i = Object.keys(t)).length; n < r; n += 1) e[o = i[n]] = t[o]; + return e + } + }, function(e, t, n) { + "use strict"; + var r = n(85), + o = n(107), + i = n(16); + + function a(e, t, n) { + var r = []; + return e.include.forEach(function(e) { + n = a(e, t, n) + }), e[t].forEach(function(e) { + n.forEach(function(t, n) { + t.tag === e.tag && t.kind === e.kind && r.push(n) + }), n.push(e) + }), n.filter(function(e, t) { + return -1 === r.indexOf(t) + }) + } + + function u(e) { + this.include = e.include || [], this.implicit = e.implicit || [], this.explicit = e.explicit || [], this.implicit.forEach(function(e) { + if (e.loadKind && "scalar" !== e.loadKind) throw new o("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.") + }), this.compiledImplicit = a(this, "implicit", []), this.compiledExplicit = a(this, "explicit", []), this.compiledTypeMap = function() { + var e, t, n = { + scalar: {}, + sequence: {}, + mapping: {}, + fallback: {} + }; + + function r(e) { + n[e.kind][e.tag] = n.fallback[e.tag] = e + } + for (e = 0, t = arguments.length; e < t; e += 1) arguments[e].forEach(r); + return n + }(this.compiledImplicit, this.compiledExplicit) + } + u.DEFAULT = null, u.create = function() { + var e, t; + switch (arguments.length) { + case 1: + e = u.DEFAULT, t = arguments[0]; + break; + case 2: + e = arguments[0], t = arguments[1]; + break; + default: + throw new o("Wrong number of arguments for Schema.create function") + } + if (e = r.toArray(e), t = r.toArray(t), !e.every(function(e) { + return e instanceof u + })) throw new o("Specified list of super schemas (or a single Schema object) contains a non-Schema object."); + if (!t.every(function(e) { + return e instanceof i + })) throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object."); + return new u({ + include: e, + explicit: t + }) + }, e.exports = u + }, function(e, t, n) { + "use strict"; + var r = n(11); + n(8); + + function o(e, t) { + return (e & t) === t + } + var i = { + MUST_USE_PROPERTY: 1, + HAS_BOOLEAN_VALUE: 4, + HAS_NUMERIC_VALUE: 8, + HAS_POSITIVE_NUMERIC_VALUE: 24, + HAS_OVERLOADED_BOOLEAN_VALUE: 32, + injectDOMPropertyConfig: function(e) { + var t = i, + n = e.Properties || {}, + a = e.DOMAttributeNamespaces || {}, + s = e.DOMAttributeNames || {}, + l = e.DOMPropertyNames || {}, + c = e.DOMMutationMethods || {}; + for (var f in e.isCustomAttribute && u._isCustomAttributeFunctions.push(e.isCustomAttribute), n) { + u.properties.hasOwnProperty(f) && r("48", f); + var p = f.toLowerCase(), + d = n[f], + h = { + attributeName: p, + attributeNamespace: null, + propertyName: f, + mutationMethod: null, + mustUseProperty: o(d, t.MUST_USE_PROPERTY), + hasBooleanValue: o(d, t.HAS_BOOLEAN_VALUE), + hasNumericValue: o(d, t.HAS_NUMERIC_VALUE), + hasPositiveNumericValue: o(d, t.HAS_POSITIVE_NUMERIC_VALUE), + hasOverloadedBooleanValue: o(d, t.HAS_OVERLOADED_BOOLEAN_VALUE) + }; + if (h.hasBooleanValue + h.hasNumericValue + h.hasOverloadedBooleanValue <= 1 || r("50", f), s.hasOwnProperty(f)) { + var v = s[f]; + h.attributeName = v + } + a.hasOwnProperty(f) && (h.attributeNamespace = a[f]), l.hasOwnProperty(f) && (h.propertyName = l[f]), c.hasOwnProperty(f) && (h.mutationMethod = c[f]), u.properties[f] = h + } + } + }, + a = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD", + u = { + ID_ATTRIBUTE_NAME: "data-reactid", + ROOT_ATTRIBUTE_NAME: "data-reactroot", + ATTRIBUTE_NAME_START_CHAR: a, + ATTRIBUTE_NAME_CHAR: a + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040", + properties: {}, + getPossibleStandardName: null, + _isCustomAttributeFunctions: [], + isCustomAttribute: function(e) { + for (var t = 0; t < u._isCustomAttributeFunctions.length; t++) { + if ((0, u._isCustomAttributeFunctions[t])(e)) return !0 + } + return !1 + }, + injection: i + }; + e.exports = u + }, function(e, t, n) { + "use strict"; + var r = n(783); + n(39), n(9); + + function o() { + r.attachRefs(this, this._currentElement) + } + var i = { + mountComponent: function(e, t, n, r, i, a) { + var u = e.mountComponent(t, n, r, i, a); + return e._currentElement && null != e._currentElement.ref && t.getReactMountReady().enqueue(o, e), u + }, + getHostNode: function(e) { + return e.getHostNode() + }, + unmountComponent: function(e, t) { + r.detachRefs(e, e._currentElement), e.unmountComponent(t) + }, + receiveComponent: function(e, t, n, i) { + var a = e._currentElement; + if (t !== a || i !== e._context) { + 0; + var u = r.shouldUpdateRefs(a, t); + u && r.detachRefs(e, a), e.receiveComponent(t, n, i), u && e._currentElement && null != e._currentElement.ref && n.getReactMountReady().enqueue(o, e) + } + }, + performUpdateIfNecessary: function(e, t, n) { + e._updateBatchNumber === n && e.performUpdateIfNecessary(t) + } + }; + e.exports = i + }, function(e, t, n) { + "use strict"; + var r = n(217), + o = n(149), + i = n(218), + a = n(359), + u = "undefined" != typeof document && "number" == typeof document.documentMode || "undefined" != typeof navigator && "string" == typeof navigator.userAgent && /\bEdge\/\d/.test(navigator.userAgent); + + function s(e) { + if (u) { + var t = e.node, + n = e.children; + if (n.length) + for (var r = 0; r < n.length; r++) l(t, n[r], null); + else null != e.html ? o(t, e.html) : null != e.text && a(t, e.text) + } + } + var l = i(function(e, t, n) { + 11 === t.node.nodeType || 1 === t.node.nodeType && "object" === t.node.nodeName.toLowerCase() && (null == t.node.namespaceURI || t.node.namespaceURI === r.html) ? (s(t), e.insertBefore(t.node, n)) : (e.insertBefore(t.node, n), s(t)) + }); + + function c() { + return this.node.nodeName + } + + function f(e) { + return { + node: e, + children: [], + html: null, + text: null, + toString: c + } + } + f.insertTreeBefore = l, f.replaceChildWithTree = function(e, t) { + e.parentNode.replaceChild(t.node, e), s(t) + }, f.queueChild = function(e, t) { + u ? e.children.push(t) : e.node.appendChild(t.node) + }, f.queueHTML = function(e, t) { + u ? e.html = t : o(e.node, t) + }, f.queueText = function(e, t) { + u ? e.text = t : a(e.node, t) + }, e.exports = f + }, function(e, t, n) { + var r = n(146), + o = n(344); + e.exports = function(e, t, n, i) { + var a = !n; + n || (n = {}); + for (var u = -1, s = t.length; ++u < s;) { + var l = t[u], + c = i ? i(n[l], e[l], l, n, e) : void 0; + void 0 === c && (c = e[l]), a ? o(n, l, c) : r(n, l, c) + } + return n + } + }, function(e, t, n) { + e.exports = { + default: n(447), + __esModule: !0 + } + }, function(e, t, n) { + n(448); + for (var r = n(21), o = n(50), i = n(70), a = n(19)("toStringTag"), u = "CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","), s = 0; s < u.length; s++) { + var l = u[s], + c = r[l], + f = c && c.prototype; + f && !f[a] && o(f, a, l), i[l] = i.Array + } + }, function(e, t) { + var n = {}.toString; + e.exports = function(e) { + return n.call(e).slice(8, -1) + } + }, function(e, t) { + e.exports = function(e) { + if ("function" != typeof e) throw TypeError(e + " is not a function!"); + return e + } + }, function(e, t) { + e.exports = function(e, t) { + return { + enumerable: !(1 & e), + configurable: !(2 & e), + writable: !(4 & e), + value: t + } + } + }, function(e, t, n) { + var r = n(239), + o = n(163); + e.exports = Object.keys || function(e) { + return r(e, o) + } + }, function(e, t, n) { + var r = n(40).f, + o = n(52), + i = n(19)("toStringTag"); + e.exports = function(e, t, n) { + e && !o(e = n ? e : e.prototype, i) && r(e, i, { + configurable: !0, + value: t + }) + } + }, function(e, t, n) { + "use strict"; + var r = n(455)(!0); + n(237)(String, "String", function(e) { + this._t = String(e), this._i = 0 + }, function() { + var e, t = this._t, + n = this._i; + return n >= t.length ? { + value: void 0, + done: !0 + } : (e = r(t, n), this._i += e.length, { + value: e, + done: !1 + }) + }) + }, function(e, t) { + var n = {}.toString; + e.exports = function(e) { + return n.call(e).slice(8, -1) + } + }, function(e, t, n) { + e.exports = !n(101)(function() { + return 7 != Object.defineProperty({}, "a", { + get: function() { + return 7 + } + }).a + }) + }, function(e, t) { + e.exports = function(e) { + try { + return !!e() + } catch (e) { + return !0 + } + } + }, function(e, t) { + e.exports = {} + }, function(e, t, n) { + var r = n(119), + o = Math.min; + e.exports = function(e) { + return e > 0 ? o(r(e), 9007199254740991) : 0 + } + }, function(e, t, n) { + "use strict"; + e.exports = function(e) { + for (var t = arguments.length - 1, n = "Minified React error #" + e + "; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=" + e, r = 0; r < t; r++) n += "&args[]=" + encodeURIComponent(arguments[r + 1]); + n += " for the full message or use the non-minified dev environment for full errors and additional helpful warnings."; + var o = new Error(n); + throw o.name = "Invariant Violation", o.framesToPop = 1, o + } + }, function(e, t) { + e.exports = function(e, t) { + return e === t || e != e && t != t + } + }, function(e, t, n) { + (function(e) { + function n(e) { + return Object.prototype.toString.call(e) + } + t.isArray = function(e) { + return Array.isArray ? Array.isArray(e) : "[object Array]" === n(e) + }, t.isBoolean = function(e) { + return "boolean" == typeof e + }, t.isNull = function(e) { + return null === e + }, t.isNullOrUndefined = function(e) { + return null == e + }, t.isNumber = function(e) { + return "number" == typeof e + }, t.isString = function(e) { + return "string" == typeof e + }, t.isSymbol = function(e) { + return "symbol" == typeof e + }, t.isUndefined = function(e) { + return void 0 === e + }, t.isRegExp = function(e) { + return "[object RegExp]" === n(e) + }, t.isObject = function(e) { + return "object" == typeof e && null !== e + }, t.isDate = function(e) { + return "[object Date]" === n(e) + }, t.isError = function(e) { + return "[object Error]" === n(e) || e instanceof Error + }, t.isFunction = function(e) { + return "function" == typeof e + }, t.isPrimitive = function(e) { + return null === e || "boolean" == typeof e || "number" == typeof e || "string" == typeof e || "symbol" == typeof e || void 0 === e + }, t.isBuffer = e.isBuffer + }).call(t, n(54).Buffer) + }, function(e, t, n) { + "use strict"; + + function r(e, t) { + Error.call(this), this.name = "YAMLException", this.reason = e, this.mark = t, this.message = (this.reason || "(unknown reason)") + (this.mark ? " " + this.mark.toString() : ""), Error.captureStackTrace ? Error.captureStackTrace(this, this.constructor) : this.stack = (new Error).stack || "" + } + r.prototype = Object.create(Error.prototype), r.prototype.constructor = r, r.prototype.toString = function(e) { + var t = this.name + ": "; + return t += this.reason || "(unknown reason)", !e && this.mark && (t += " " + this.mark.toString()), t + }, e.exports = r + }, function(e, t, n) { + "use strict"; + var r = n(86); + e.exports = new r({ + include: [n(340)], + implicit: [n(751), n(752)], + explicit: [n(753), n(754), n(755), n(756)] + }) + }, function(e, t, n) { + "use strict"; + var r = n(110), + o = n(211), + i = n(351), + a = n(352), + u = (n(9), r.getListener); + + function s(e, t, n) { + var r = function(e, t, n) { + var r = t.dispatchConfig.phasedRegistrationNames[n]; + return u(e, r) + }(e, n, t); + r && (n._dispatchListeners = i(n._dispatchListeners, r), n._dispatchInstances = i(n._dispatchInstances, e)) + } + + function l(e) { + e && e.dispatchConfig.phasedRegistrationNames && o.traverseTwoPhase(e._targetInst, s, e) + } + + function c(e) { + if (e && e.dispatchConfig.phasedRegistrationNames) { + var t = e._targetInst, + n = t ? o.getParentInstance(t) : null; + o.traverseTwoPhase(n, s, e) + } + } + + function f(e, t, n) { + if (n && n.dispatchConfig.registrationName) { + var r = n.dispatchConfig.registrationName, + o = u(e, r); + o && (n._dispatchListeners = i(n._dispatchListeners, o), n._dispatchInstances = i(n._dispatchInstances, e)) + } + } + + function p(e) { + e && e.dispatchConfig.registrationName && f(e._targetInst, 0, e) + } + var d = { + accumulateTwoPhaseDispatches: function(e) { + a(e, l) + }, + accumulateTwoPhaseDispatchesSkipTarget: function(e) { + a(e, c) + }, + accumulateDirectDispatches: function(e) { + a(e, p) + }, + accumulateEnterLeaveDispatches: function(e, t, n, r) { + o.traverseEnterLeave(n, r, f, e, t) + } + }; + e.exports = d + }, function(e, t, n) { + "use strict"; + var r = n(11), + o = n(210), + i = n(211), + a = n(212), + u = n(351), + s = n(352), + l = (n(8), {}), + c = null, + f = function(e, t) { + e && (i.executeDispatchesInOrder(e, t), e.isPersistent() || e.constructor.release(e)) + }, + p = function(e) { + return f(e, !0) + }, + d = function(e) { + return f(e, !1) + }, + h = function(e) { + return "." + e._rootNodeID + }; + var v = { + injection: { + injectEventPluginOrder: o.injectEventPluginOrder, + injectEventPluginsByName: o.injectEventPluginsByName + }, + putListener: function(e, t, n) { + "function" != typeof n && r("94", t, typeof n); + var i = h(e); + (l[t] || (l[t] = {}))[i] = n; + var a = o.registrationNameModules[t]; + a && a.didPutListener && a.didPutListener(e, t, n) + }, + getListener: function(e, t) { + var n = l[t]; + if (function(e, t, n) { + switch (e) { + case "onClick": + case "onClickCapture": + case "onDoubleClick": + case "onDoubleClickCapture": + case "onMouseDown": + case "onMouseDownCapture": + case "onMouseMove": + case "onMouseMoveCapture": + case "onMouseUp": + case "onMouseUpCapture": + return !(!n.disabled || (r = t, "button" !== r && "input" !== r && "select" !== r && "textarea" !== r)); + default: + return !1 + } + var r + }(t, e._currentElement.type, e._currentElement.props)) return null; + var r = h(e); + return n && n[r] + }, + deleteListener: function(e, t) { + var n = o.registrationNameModules[t]; + n && n.willDeleteListener && n.willDeleteListener(e, t); + var r = l[t]; + r && delete r[h(e)] + }, + deleteAllListeners: function(e) { + var t = h(e); + for (var n in l) + if (l.hasOwnProperty(n) && l[n][t]) { + var r = o.registrationNameModules[n]; + r && r.willDeleteListener && r.willDeleteListener(e, n), delete l[n][t] + } + }, + extractEvents: function(e, t, n, r) { + for (var i, a = o.plugins, s = 0; s < a.length; s++) { + var l = a[s]; + if (l) { + var c = l.extractEvents(e, t, n, r); + c && (i = u(i, c)) + } + } + return i + }, + enqueueEvents: function(e) { + e && (c = u(c, e)) + }, + processEventQueue: function(e) { + var t = c; + c = null, s(t, e ? p : d), c && r("95"), a.rethrowCaughtError() + }, + __purge: function() { + l = {} + }, + __getListenerBank: function() { + return l + } + }; + e.exports = v + }, function(e, t, n) { + "use strict"; + var r = n(48), + o = n(213), + i = { + view: function(e) { + if (e.view) return e.view; + var t = o(e); + if (t.window === t) return t; + var n = t.ownerDocument; + return n ? n.defaultView || n.parentWindow : window + }, + detail: function(e) { + return e.detail || 0 + } + }; + + function a(e, t, n, o) { + return r.call(this, e, t, n, o) + } + r.augmentClass(a, i), e.exports = a + }, function(e, t, n) { + "use strict"; + var r = { + remove: function(e) { + e._reactInternalInstance = void 0 + }, + get: function(e) { + return e._reactInternalInstance + }, + has: function(e) { + return void 0 !== e._reactInternalInstance + }, + set: function(e, t) { + e._reactInternalInstance = t + } + }; + e.exports = r + }, function(e, t, n) { + var r; + /*! + Copyright (c) 2016 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/classnames + */ + /*! + Copyright (c) 2016 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/classnames + */ + ! function() { + "use strict"; + var n = {}.hasOwnProperty; + + function o() { + for (var e = [], t = 0; t < arguments.length; t++) { + var r = arguments[t]; + if (r) { + var i = typeof r; + if ("string" === i || "number" === i) e.push(r); + else if (Array.isArray(r)) e.push(o.apply(null, r)); + else if ("object" === i) + for (var a in r) n.call(r, a) && r[a] && e.push(a) + } + } + return e.join(" ") + } + void 0 !== e && e.exports ? e.exports = o : void 0 === (r = function() { + return o + }.apply(t, [])) || (e.exports = r) + }() + }, function(e, t) { + e.exports = !0 + }, function(e, t, n) { + var r = n(160), + o = Math.min; + e.exports = function(e) { + return e > 0 ? o(r(e), 9007199254740991) : 0 + } + }, function(e, t) { + var n = 0, + r = Math.random(); + e.exports = function(e) { + return "Symbol(".concat(void 0 === e ? "" : e, ")_", (++n + r).toString(36)) + } + }, function(e, t, n) { + var r = n(59), + o = n(459), + i = n(460), + a = Object.defineProperty; + t.f = n(100) ? Object.defineProperty : function(e, t, n) { + if (r(e), t = i(t, !0), r(n), o) try { + return a(e, t, n) + } catch (e) {} + if ("get" in n || "set" in n) throw TypeError("Accessors not supported!"); + return "value" in n && (e[t] = n.value), e + } + }, function(e, t) { + var n = {}.hasOwnProperty; + e.exports = function(e, t) { + return n.call(e, t) + } + }, function(e, t) { + var n = Math.ceil, + r = Math.floor; + e.exports = function(e) { + return isNaN(e = +e) ? 0 : (e > 0 ? r : n)(e) + } + }, function(e, t, n) { + var r = n(121); + e.exports = function(e, t, n) { + if (r(e), void 0 === t) return e; + switch (n) { + case 1: + return function(n) { + return e.call(t, n) + }; + case 2: + return function(n, r) { + return e.call(t, n, r) + }; + case 3: + return function(n, r, o) { + return e.call(t, n, r, o) + } + } + return function() { + return e.apply(t, arguments) + } + } + }, function(e, t) { + e.exports = function(e) { + if ("function" != typeof e) throw TypeError(e + " is not a function!"); + return e + } + }, function(e, t, n) { + var r = n(465), + o = n(53); + e.exports = function(e) { + return r(o(e)) + } + }, function(e, t, n) { + "use strict"; + var r = n(58), + o = n(73), + i = n(101), + a = n(53), + u = n(17); + e.exports = function(e, t, n) { + var s = u(e), + l = n(a, s, "" [e]), + c = l[0], + f = l[1]; + i(function() { + var t = {}; + return t[s] = function() { + return 7 + }, 7 != "" [e](t) + }) && (o(String.prototype, e, c), r(RegExp.prototype, s, 2 == t ? function(e, t) { + return f.call(e, this, t) + } : function(e) { + return f.call(e, this) + })) + } + }, function(e, t, n) { + var r = n(116)("meta"), + o = n(28), + i = n(52), + a = n(40).f, + u = 0, + s = Object.isExtensible || function() { + return !0 + }, + l = !n(51)(function() { + return s(Object.preventExtensions({})) + }), + c = function(e) { + a(e, r, { + value: { + i: "O" + ++u, + w: {} + } + }) + }, + f = e.exports = { + KEY: r, + NEED: !1, + fastKey: function(e, t) { + if (!o(e)) return "symbol" == typeof e ? e : ("string" == typeof e ? "S" : "P") + e; + if (!i(e, r)) { + if (!s(e)) return "F"; + if (!t) return "E"; + c(e) + } + return e[r].i + }, + getWeak: function(e, t) { + if (!i(e, r)) { + if (!s(e)) return !0; + if (!t) return !1; + c(e) + } + return e[r].w + }, + onFreeze: function(e) { + return l && f.NEED && s(e) && !i(e, r) && c(e), e + } + } + }, function(e, t) { + t.f = {}.propertyIsEnumerable + }, function(e, t, n) { + "use strict"; + var r = {}; + e.exports = r + }, function(e, t, n) { + "use strict"; + Object.defineProperty(t, "__esModule", { + value: !0 + }), t.CLEAR_BY = t.CLEAR = t.NEW_AUTH_ERR = t.NEW_SPEC_ERR_BATCH = t.NEW_SPEC_ERR = t.NEW_THROWN_ERR_BATCH = t.NEW_THROWN_ERR = void 0, t.newThrownErr = function(e) { + return { + type: a, + payload: (0, i.default)(e) + } + }, t.newThrownErrBatch = function(e) { + return { + type: u, + payload: e + } + }, t.newSpecErr = function(e) { + return { + type: s, + payload: e + } + }, t.newSpecErrBatch = function(e) { + return { + type: l, + payload: e + } + }, t.newAuthErr = function(e) { + return { + type: c, + payload: e + } + }, t.clear = function() { + var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; + return { + type: f, + payload: e + } + }, t.clearBy = function() { + var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : function() { + return !0 + }; + return { + type: p, + payload: e + } + }; + var r, o = n(179), + i = (r = o) && r.__esModule ? r : { + default: r + }; + var a = t.NEW_THROWN_ERR = "err_new_thrown_err", + u = t.NEW_THROWN_ERR_BATCH = "err_new_thrown_err_batch", + s = t.NEW_SPEC_ERR = "err_new_spec_err", + l = t.NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch", + c = t.NEW_AUTH_ERR = "err_new_auth_err", + f = t.CLEAR = "err_clear", + p = t.CLEAR_BY = "err_clear_by" + }, function(e, t, n) { + var r = n(62), + o = n(47), + i = "[object Symbol]"; + e.exports = function(e) { + return "symbol" == typeof e || o(e) && r(e) == i + } + }, function(e, t, n) { + var r = n(63)(Object, "create"); + e.exports = r + }, function(e, t, n) { + var r = n(600), + o = n(601), + i = n(602), + a = n(603), + u = n(604); + + function s(e) { + var t = -1, + n = null == e ? 0 : e.length; + for (this.clear(); ++t < n;) { + var r = e[t]; + this.set(r[0], r[1]) + } + } + s.prototype.clear = r, s.prototype.delete = o, s.prototype.get = i, s.prototype.has = a, s.prototype.set = u, e.exports = s + }, function(e, t, n) { + var r = n(105); + e.exports = function(e, t) { + for (var n = e.length; n--;) + if (r(e[n][0], t)) return n; + return -1 + } + }, function(e, t, n) { + var r = n(606); + e.exports = function(e, t) { + var n = e.__data__; + return r(t) ? n["string" == typeof t ? "string" : "hash"] : n.map + } + }, function(e, t, n) { + var r = n(611), + o = n(639), + i = n(191), + a = n(24), + u = n(644); + e.exports = function(e) { + return "function" == typeof e ? e : null == e ? i : "object" == typeof e ? a(e) ? o(e[0], e[1]) : r(e) : u(e) + } + }, function(e, t) { + e.exports = function(e) { + return e.webpackPolyfill || (e.deprecate = function() {}, e.paths = [], e.children || (e.children = []), Object.defineProperty(e, "loaded", { + enumerable: !0, + get: function() { + return e.l + } + }), Object.defineProperty(e, "id", { + enumerable: !0, + get: function() { + return e.i + } + }), e.webpackPolyfill = 1), e + } + }, function(e, t) { + var n = 9007199254740991, + r = /^(?:0|[1-9]\d*)$/; + e.exports = function(e, t) { + var o = typeof e; + return !!(t = null == t ? n : t) && ("number" == o || "symbol" != o && r.test(e)) && e > -1 && e % 1 == 0 && e < t + } + }, function(e, t) { + var n = Object.prototype; + e.exports = function(e) { + var t = e && e.constructor; + return e === ("function" == typeof t && t.prototype || n) + } + }, function(e, t, n) { + var r = n(634), + o = n(181), + i = n(635), + a = n(636), + u = n(637), + s = n(62), + l = n(286), + c = l(r), + f = l(o), + p = l(i), + d = l(a), + h = l(u), + v = s; + (r && "[object DataView]" != v(new r(new ArrayBuffer(1))) || o && "[object Map]" != v(new o) || i && "[object Promise]" != v(i.resolve()) || a && "[object Set]" != v(new a) || u && "[object WeakMap]" != v(new u)) && (v = function(e) { + var t = s(e), + n = "[object Object]" == t ? e.constructor : void 0, + r = n ? l(n) : ""; + if (r) switch (r) { + case c: + return "[object DataView]"; + case f: + return "[object Map]"; + case p: + return "[object Promise]"; + case d: + return "[object Set]"; + case h: + return "[object WeakMap]" + } + return t + }), e.exports = v + }, function(e, t, n) { + var r = n(139); + e.exports = function(e, t, n) { + var o = null == e ? void 0 : r(e, t); + return void 0 === o ? n : o + } + }, function(e, t, n) { + var r = n(79), + o = n(80); + e.exports = function(e, t) { + for (var n = 0, i = (t = r(t, e)).length; null != e && n < i;) e = e[o(t[n++])]; + return n && n == i ? e : void 0 + } + }, function(e, t, n) { + "use strict"; + (function(t) { + !t.version || 0 === t.version.indexOf("v0.") || 0 === t.version.indexOf("v1.") && 0 !== t.version.indexOf("v1.8.") ? e.exports = { + nextTick: function(e, n, r, o) { + if ("function" != typeof e) throw new TypeError('"callback" argument must be a function'); + var i, a, u = arguments.length; + switch (u) { + case 0: + case 1: + return t.nextTick(e); + case 2: + return t.nextTick(function() { + e.call(null, n) + }); + case 3: + return t.nextTick(function() { + e.call(null, n, r) + }); + case 4: + return t.nextTick(function() { + e.call(null, n, r, o) + }); + default: + for (i = new Array(u - 1), a = 0; a < i.length;) i[a++] = arguments[a]; + return t.nextTick(function() { + e.apply(null, i) + }) + } + } + } : e.exports = t + }).call(t, n(55)) + }, function(e, t, n) { + var r = n(54), + o = r.Buffer; + + function i(e, t) { + for (var n in e) t[n] = e[n] + } + + function a(e, t, n) { + return o(e, t, n) + } + o.from && o.alloc && o.allocUnsafe && o.allocUnsafeSlow ? e.exports = r : (i(r, t), t.Buffer = a), i(o, a), a.from = function(e, t, n) { + if ("number" == typeof e) throw new TypeError("Argument must not be a number"); + return o(e, t, n) + }, a.alloc = function(e, t, n) { + if ("number" != typeof e) throw new TypeError("Argument must be a number"); + var r = o(e); + return void 0 !== t ? "string" == typeof n ? r.fill(t, n) : r.fill(t) : r.fill(0), r + }, a.allocUnsafe = function(e) { + if ("number" != typeof e) throw new TypeError("Argument must be a number"); + return o(e) + }, a.allocUnsafeSlow = function(e) { + if ("number" != typeof e) throw new TypeError("Argument must be a number"); + return r.SlowBuffer(e) + } + }, function(e, t, n) { + "use strict"; + e.exports = n(675)("forEach") + }, function(e, t, n) { + "use strict"; + var r = n(314), + o = n(311), + i = n(196), + a = n(684); + (e.exports = function(e, t) { + var n, i, u, s, l; + return arguments.length < 2 || "string" != typeof e ? (s = t, t = e, e = null) : s = arguments[2], null == e ? (n = u = !0, i = !1) : (n = a.call(e, "c"), i = a.call(e, "e"), u = a.call(e, "w")), l = { + value: t, + configurable: n, + enumerable: i, + writable: u + }, s ? r(o(s), l) : l + }).gs = function(e, t, n) { + var u, s, l, c; + return "string" != typeof e ? (l = n, n = t, t = e, e = null) : l = arguments[3], null == t ? t = void 0 : i(t) ? null == n ? n = void 0 : i(n) || (l = n, n = void 0) : (l = t, t = n = void 0), null == e ? (u = !0, s = !1) : (u = a.call(e, "c"), s = a.call(e, "e")), c = { + get: t, + set: n, + configurable: u, + enumerable: s + }, l ? r(o(l), c) : c + } + }, function(e, t, n) { + var r = n(49), + o = n(329), + i = n(330), + a = n(36), + u = n(115), + s = n(164), + l = {}, + c = {}; + (t = e.exports = function(e, t, n, f, p) { + var d, h, v, m, g = p ? function() { + return e + } : s(e), + y = r(n, f, t ? 2 : 1), + b = 0; + if ("function" != typeof g) throw TypeError(e + " is not iterable!"); + if (i(g)) { + for (d = u(e.length); d > b; b++) + if ((m = t ? y(a(h = e[b])[0], h[1]) : y(e[b])) === l || m === c) return m + } else + for (v = g.call(e); !(h = v.next()).done;) + if ((m = o(v, y, h.value, t)) === l || m === c) return m + }).BREAK = l, t.RETURN = c + }, function(e, t, n) { + "use strict"; + var r = n(86); + e.exports = r.DEFAULT = new r({ + include: [n(108)], + explicit: [n(757), n(758), n(759)] + }) + }, function(e, t, n) { + var r = n(344), + o = n(105), + i = Object.prototype.hasOwnProperty; + e.exports = function(e, t, n) { + var a = e[t]; + i.call(e, t) && o(a, n) && (void 0 !== n || t in e) || r(e, t, n) + } + }, function(e, t, n) { + "use strict"; + var r = n(11), + o = (n(8), {}), + i = { + reinitializeTransaction: function() { + this.transactionWrappers = this.getTransactionWrappers(), this.wrapperInitData ? this.wrapperInitData.length = 0 : this.wrapperInitData = [], this._isInTransaction = !1 + }, + _isInTransaction: !1, + getTransactionWrappers: null, + isInTransaction: function() { + return !!this._isInTransaction + }, + perform: function(e, t, n, o, i, a, u, s) { + var l, c; + this.isInTransaction() && r("27"); + try { + this._isInTransaction = !0, l = !0, this.initializeAll(0), c = e.call(t, n, o, i, a, u, s), l = !1 + } finally { + try { + if (l) try { + this.closeAll(0) + } catch (e) {} else this.closeAll(0) + } finally { + this._isInTransaction = !1 + } + } + return c + }, + initializeAll: function(e) { + for (var t = this.transactionWrappers, n = e; n < t.length; n++) { + var r = t[n]; + try { + this.wrapperInitData[n] = o, this.wrapperInitData[n] = r.initialize ? r.initialize.call(this) : null + } finally { + if (this.wrapperInitData[n] === o) try { + this.initializeAll(n + 1) + } catch (e) {} + } + } + }, + closeAll: function(e) { + this.isInTransaction() || r("28"); + for (var t = this.transactionWrappers, n = e; n < t.length; n++) { + var i, a = t[n], + u = this.wrapperInitData[n]; + try { + i = !0, u !== o && a.close && a.close.call(this, u), i = !1 + } finally { + if (i) try { + this.closeAll(n + 1) + } catch (e) {} + } + } + this.wrapperInitData.length = 0 + } + }; + e.exports = i + }, function(e, t, n) { + "use strict"; + var r = n(111), + o = n(358), + i = { + screenX: null, + screenY: null, + clientX: null, + clientY: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + getModifierState: n(215), + button: function(e) { + var t = e.button; + return "which" in e ? t : 2 === t ? 2 : 4 === t ? 1 : 0 + }, + buttons: null, + relatedTarget: function(e) { + return e.relatedTarget || (e.fromElement === e.srcElement ? e.toElement : e.fromElement) + }, + pageX: function(e) { + return "pageX" in e ? e.pageX : e.clientX + o.currentScrollLeft + }, + pageY: function(e) { + return "pageY" in e ? e.pageY : e.clientY + o.currentScrollTop + } + }; + + function a(e, t, n, o) { + return r.call(this, e, t, n, o) + } + r.augmentClass(a, i), e.exports = a + }, function(e, t, n) { + "use strict"; + var r, o = n(26), + i = n(217), + a = /^[ \r\n\t\f]/, + u = /<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/, + s = n(218)(function(e, t) { + if (e.namespaceURI !== i.svg || "innerHTML" in e) e.innerHTML = t; + else { + (r = r || document.createElement("div")).innerHTML = "" + t + ""; + for (var n = r.firstChild; n.firstChild;) e.appendChild(n.firstChild) + } + }); + if (o.canUseDOM) { + var l = document.createElement("div"); + l.innerHTML = " ", "" === l.innerHTML && (s = function(e, t) { + if (e.parentNode && e.parentNode.replaceChild(e, e), a.test(t) || "<" === t[0] && u.test(t)) { + e.innerHTML = String.fromCharCode(65279) + t; + var n = e.firstChild; + 1 === n.data.length ? e.removeChild(n) : n.deleteData(0, 1) + } else e.innerHTML = t + }), l = null + } + e.exports = s + }, function(e, t, n) { + "use strict"; + var r = /["'&<>]/; + e.exports = function(e) { + return "boolean" == typeof e || "number" == typeof e ? "" + e : function(e) { + var t, n = "" + e, + o = r.exec(n); + if (!o) return n; + var i = "", + a = 0, + u = 0; + for (a = o.index; a < n.length; a++) { + switch (n.charCodeAt(a)) { + case 34: + t = """; + break; + case 38: + t = "&"; + break; + case 39: + t = "'"; + break; + case 60: + t = "<"; + break; + case 62: + t = ">"; + break; + default: + continue + } + u !== a && (i += n.substring(u, a)), u = a + 1, i += t + } + return u !== a ? i + n.substring(u, a) : i + }(e) + } + }, function(e, t, n) { + "use strict"; + var r, o = n(13), + i = n(210), + a = n(804), + u = n(358), + s = n(805), + l = n(214), + c = {}, + f = !1, + p = 0, + d = { + topAbort: "abort", + topAnimationEnd: s("animationend") || "animationend", + topAnimationIteration: s("animationiteration") || "animationiteration", + topAnimationStart: s("animationstart") || "animationstart", + topBlur: "blur", + topCanPlay: "canplay", + topCanPlayThrough: "canplaythrough", + topChange: "change", + topClick: "click", + topCompositionEnd: "compositionend", + topCompositionStart: "compositionstart", + topCompositionUpdate: "compositionupdate", + topContextMenu: "contextmenu", + topCopy: "copy", + topCut: "cut", + topDoubleClick: "dblclick", + topDrag: "drag", + topDragEnd: "dragend", + topDragEnter: "dragenter", + topDragExit: "dragexit", + topDragLeave: "dragleave", + topDragOver: "dragover", + topDragStart: "dragstart", + topDrop: "drop", + topDurationChange: "durationchange", + topEmptied: "emptied", + topEncrypted: "encrypted", + topEnded: "ended", + topError: "error", + topFocus: "focus", + topInput: "input", + topKeyDown: "keydown", + topKeyPress: "keypress", + topKeyUp: "keyup", + topLoadedData: "loadeddata", + topLoadedMetadata: "loadedmetadata", + topLoadStart: "loadstart", + topMouseDown: "mousedown", + topMouseMove: "mousemove", + topMouseOut: "mouseout", + topMouseOver: "mouseover", + topMouseUp: "mouseup", + topPaste: "paste", + topPause: "pause", + topPlay: "play", + topPlaying: "playing", + topProgress: "progress", + topRateChange: "ratechange", + topScroll: "scroll", + topSeeked: "seeked", + topSeeking: "seeking", + topSelectionChange: "selectionchange", + topStalled: "stalled", + topSuspend: "suspend", + topTextInput: "textInput", + topTimeUpdate: "timeupdate", + topTouchCancel: "touchcancel", + topTouchEnd: "touchend", + topTouchMove: "touchmove", + topTouchStart: "touchstart", + topTransitionEnd: s("transitionend") || "transitionend", + topVolumeChange: "volumechange", + topWaiting: "waiting", + topWheel: "wheel" + }, + h = "_reactListenersID" + String(Math.random()).slice(2); + var v = o({}, a, { + ReactEventListener: null, + injection: { + injectReactEventListener: function(e) { + e.setHandleTopLevel(v.handleTopLevel), v.ReactEventListener = e + } + }, + setEnabled: function(e) { + v.ReactEventListener && v.ReactEventListener.setEnabled(e) + }, + isEnabled: function() { + return !(!v.ReactEventListener || !v.ReactEventListener.isEnabled()) + }, + listenTo: function(e, t) { + for (var n = t, r = function(e) { + return Object.prototype.hasOwnProperty.call(e, h) || (e[h] = p++, c[e[h]] = {}), c[e[h]] + }(n), o = i.registrationNameDependencies[e], a = 0; a < o.length; a++) { + var u = o[a]; + r.hasOwnProperty(u) && r[u] || ("topWheel" === u ? l("wheel") ? v.ReactEventListener.trapBubbledEvent("topWheel", "wheel", n) : l("mousewheel") ? v.ReactEventListener.trapBubbledEvent("topWheel", "mousewheel", n) : v.ReactEventListener.trapBubbledEvent("topWheel", "DOMMouseScroll", n) : "topScroll" === u ? l("scroll", !0) ? v.ReactEventListener.trapCapturedEvent("topScroll", "scroll", n) : v.ReactEventListener.trapBubbledEvent("topScroll", "scroll", v.ReactEventListener.WINDOW_HANDLE) : "topFocus" === u || "topBlur" === u ? (l("focus", !0) ? (v.ReactEventListener.trapCapturedEvent("topFocus", "focus", n), v.ReactEventListener.trapCapturedEvent("topBlur", "blur", n)) : l("focusin") && (v.ReactEventListener.trapBubbledEvent("topFocus", "focusin", n), v.ReactEventListener.trapBubbledEvent("topBlur", "focusout", n)), r.topBlur = !0, r.topFocus = !0) : d.hasOwnProperty(u) && v.ReactEventListener.trapBubbledEvent(u, d[u], n), r[u] = !0) + } + }, + trapBubbledEvent: function(e, t, n) { + return v.ReactEventListener.trapBubbledEvent(e, t, n) + }, + trapCapturedEvent: function(e, t, n) { + return v.ReactEventListener.trapCapturedEvent(e, t, n) + }, + supportsEventPageXY: function() { + if (!document.createEvent) return !1; + var e = document.createEvent("MouseEvent"); + return null != e && "pageX" in e + }, + ensureScrollValueMonitoring: function() { + if (void 0 === r && (r = v.supportsEventPageXY()), !r && !f) { + var e = u.refreshScrollValues; + v.ReactEventListener.monitorScrollValue(e), f = !0 + } + } + }); + e.exports = v + }, function(e, t, n) { + "use strict"; + + function r() { + this.__rules__ = [], this.__cache__ = null + } + r.prototype.__find__ = function(e) { + for (var t = this.__rules__.length, n = -1; t--;) + if (this.__rules__[++n].name === e) return n; + return -1 + }, r.prototype.__compile__ = function() { + var e = this, + t = [""]; + e.__rules__.forEach(function(e) { + e.enabled && e.alt.forEach(function(e) { + t.indexOf(e) < 0 && t.push(e) + }) + }), e.__cache__ = {}, t.forEach(function(t) { + e.__cache__[t] = [], e.__rules__.forEach(function(n) { + n.enabled && (t && n.alt.indexOf(t) < 0 || e.__cache__[t].push(n.fn)) + }) + }) + }, r.prototype.at = function(e, t, n) { + var r = this.__find__(e), + o = n || {}; + if (-1 === r) throw new Error("Parser rule not found: " + e); + this.__rules__[r].fn = t, this.__rules__[r].alt = o.alt || [], this.__cache__ = null + }, r.prototype.before = function(e, t, n, r) { + var o = this.__find__(e), + i = r || {}; + if (-1 === o) throw new Error("Parser rule not found: " + e); + this.__rules__.splice(o, 0, { + name: t, + enabled: !0, + fn: n, + alt: i.alt || [] + }), this.__cache__ = null + }, r.prototype.after = function(e, t, n, r) { + var o = this.__find__(e), + i = r || {}; + if (-1 === o) throw new Error("Parser rule not found: " + e); + this.__rules__.splice(o + 1, 0, { + name: t, + enabled: !0, + fn: n, + alt: i.alt || [] + }), this.__cache__ = null + }, r.prototype.push = function(e, t, n) { + var r = n || {}; + this.__rules__.push({ + name: e, + enabled: !0, + fn: t, + alt: r.alt || [] + }), this.__cache__ = null + }, r.prototype.enable = function(e, t) { + e = Array.isArray(e) ? e : [e], t && this.__rules__.forEach(function(e) { + e.enabled = !1 + }), e.forEach(function(e) { + var t = this.__find__(e); + if (t < 0) throw new Error("Rules manager: invalid rule name " + e); + this.__rules__[t].enabled = !0 + }, this), this.__cache__ = null + }, r.prototype.disable = function(e) { + (e = Array.isArray(e) ? e : [e]).forEach(function(e) { + var t = this.__find__(e); + if (t < 0) throw new Error("Rules manager: invalid rule name " + e); + this.__rules__[t].enabled = !1 + }, this), this.__cache__ = null + }, r.prototype.getRules = function(e) { + return null === this.__cache__ && this.__compile__(), this.__cache__[e] || [] + }, e.exports = r + }, function(e, t, n) { + "use strict"; + e.exports = function(e, t) { + var n, r, o, i = -1, + a = e.posMax, + u = e.pos, + s = e.isInLabel; + if (e.isInLabel) return -1; + if (e.labelUnmatchedScopes) return e.labelUnmatchedScopes--, -1; + for (e.pos = t + 1, e.isInLabel = !0, n = 1; e.pos < a;) { + if (91 === (o = e.src.charCodeAt(e.pos))) n++; + else if (93 === o && 0 === --n) { + r = !0; + break + } + e.parser.skipToken(e) + } + return r ? (i = e.pos, e.labelUnmatchedScopes = 0) : e.labelUnmatchedScopes = n - 1, e.pos = u, e.isInLabel = s, i + } + }, function(e, t, n) { + var r = n(93); + e.exports = Object("z").propertyIsEnumerable(0) ? Object : function(e) { + return "String" == r(e) ? e.split("") : Object(e) + } + }, function(e, t) { + e.exports = function(e) { + if (void 0 == e) throw TypeError("Can't call method on " + e); + return e + } + }, function(e, t, n) { + var r = n(28), + o = n(21).document, + i = r(o) && r(o.createElement); + e.exports = function(e) { + return i ? o.createElement(e) : {} + } + }, function(e, t, n) { + var r = n(28); + e.exports = function(e, t) { + if (!r(e)) return e; + var n, o; + if (t && "function" == typeof(n = e.toString) && !r(o = n.call(e))) return o; + if ("function" == typeof(n = e.valueOf) && !r(o = n.call(e))) return o; + if (!t && "function" == typeof(n = e.toString) && !r(o = n.call(e))) return o; + throw TypeError("Can't convert object to primitive value") + } + }, function(e, t, n) { + e.exports = n(50) + }, function(e, t, n) { + var r = n(36), + o = n(452), + i = n(163), + a = n(161)("IE_PROTO"), + u = function() {}, + s = function() { + var e, t = n(156)("iframe"), + r = i.length; + for (t.style.display = "none", n(240).appendChild(t), t.src = "javascript:", (e = t.contentWindow.document).open(), e.write("