diff --git a/Gopkg.lock b/Gopkg.lock index d64538d36b..00eafa4ade 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -229,6 +229,14 @@ revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" +[[projects]] + digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af" + name = "github.com/mitchellh/go-homedir" + packages = ["."] + pruneopts = "UT" + revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" + version = "v1.0.0" + [[projects]] digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4" name = "github.com/mitchellh/mapstructure" @@ -630,6 +638,8 @@ "github.com/golang/protobuf/proto", "github.com/gorilla/mux", "github.com/mattn/go-isatty", + "github.com/mitchellh/go-homedir", + "github.com/pelletier/go-toml", "github.com/pkg/errors", "github.com/spf13/cobra", "github.com/spf13/pflag", diff --git a/Gopkg.toml b/Gopkg.toml index 2b5928a897..3080e3cd35 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -70,3 +70,7 @@ [prune] go-tests = true unused-packages = true + +[[constraint]] + name = "github.com/mitchellh/go-homedir" + version = "1.0.0" diff --git a/PENDING.md b/PENDING.md index 95dcc7c47d..c432ec3e07 100644 --- a/PENDING.md +++ b/PENDING.md @@ -56,6 +56,7 @@ BREAKING CHANGES * [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging. * [baseapp] \#2366 Automatically add action tags to all messages * [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index + * [x/staking] \#2236 more distribution hooks for distribution * Tendermint @@ -81,6 +82,7 @@ FEATURES * [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. * [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag. * [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command. + * [cli] \#2220 Add `gaiacli config` feature to interactively create CLI config files to reduce the number of required flags * [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced new commission flags for validator commands `create-validator` and `edit-validator`. diff --git a/client/config.go b/client/config.go new file mode 100644 index 0000000000..fcb2523753 --- /dev/null +++ b/client/config.go @@ -0,0 +1,131 @@ +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" +) + +type cliConfig struct { + Home string `toml:"home"` + ChainID string `toml:"chain_id"` + TrustNode bool `toml:"trust_node"` + Encoding string `toml:"encoding"` + Output string `toml:"output"` + Node string `toml:"node"` + Trace bool `toml:"trace"` +} + +// ConfigCmd returns a CLI command to interactively create a +// Gaia CLI config file. +func ConfigCmd() *cobra.Command { + cfg := &cobra.Command{ + Use: "config", + Short: "Interactively creates a Gaia CLI config file", + RunE: runConfigCmd, + } + + return cfg +} + +func runConfigCmd(cmd *cobra.Command, args [] string) error { + home, err := homedir.Dir() + if err != nil { + return err + } + + stdin := BufferStdin() + gaiaCLIHome, err := handleGaiaCLIHome(home, stdin) + if err != nil { + return err + } + node, err := handleNode(stdin) + if err != nil { + return err + } + trustNode, err := handleTrustNode(stdin) + if err != nil { + return err + } + + encoding := "btc" + output := "text" + var chainID string + chainID, err = types.DefaultChainID() + if err != nil { + fmt.Println("Couldn't populate ChainID, so using an empty one.") + } + + cfg := &cliConfig{ + Home: gaiaCLIHome, + ChainID: chainID, + TrustNode: trustNode, + Encoding: encoding, + Output: output, + Node: node, + Trace: false, + } + + return createGaiaCLIConfig(cfg) +} + +func handleGaiaCLIHome(dir string, stdin *bufio.Reader) (string, error) { + dirName := ".gaiacli" + home, err := GetString(fmt.Sprintf("Where is your gaiacli home directory? (Default: ~/%s)", dirName), stdin) + if err != nil { + return "", err + } + + if home == "" { + home = path.Join(dir, dirName) + } + + return home, nil +} + +func handleNode(stdin *bufio.Reader) (string, error) { + defaultNode := "tcp://localhost:26657" + node, err := GetString(fmt.Sprintf("Where is your validator node running? (Default: %s)", defaultNode), stdin) + if err != nil { + return "", err + } + + if node == "" { + node = defaultNode + } + + return node, nil +} + +func handleTrustNode(stdin *bufio.Reader) (bool, error) { + return GetConfirmation("Do you trust this node?", stdin) +} + +func createGaiaCLIConfig(cfg *cliConfig) error { + cfgPath := path.Join(cfg.Home, "config") + err := os.MkdirAll(cfgPath, os.ModePerm) + if err != nil { + return err + } + + data, err := toml.Marshal(*cfg) + if err != nil { + return err + } + + cfgFile := path.Join(cfgPath, "config.toml") + if info, err := os.Stat(cfgFile); err == nil && !info.IsDir() { + err = os.Rename(cfgFile, path.Join(cfgPath, "config.toml-old")) + if err != nil { + return err + } + } + + return ioutil.WriteFile(cfgFile, data, os.ModePerm) +} diff --git a/client/flags.go b/client/flags.go index d0d783d3a7..ff93549370 100644 --- a/client/flags.go +++ b/client/flags.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // nolint @@ -51,6 +52,10 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block") + viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) + viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) } return cmds } @@ -76,6 +81,10 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { // --gas can accept integers and "simulate" c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf( "gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", GasFlagSimulate, DefaultGasLimit)) + viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) + viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) } return cmds } diff --git a/client/input.go b/client/input.go index b10f65ce62..a456f1b92a 100644 --- a/client/input.go +++ b/client/input.go @@ -100,6 +100,19 @@ func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) { } } +// GetString simply returns the trimmed string output of a given reader. +func GetString(prompt string, buf *bufio.Reader) (string, error) { + if inputIsTty() && prompt != "" { + PrintPrefixed(prompt) + } + + out, err := readLineFromBuf(buf) + if err != nil { + return "", err + } + return strings.TrimSpace(out), nil +} + // inputIsTty returns true iff we have an interactive prompt, // where we can disable echo and request to repeat the password. // If false, we can optimize for piped input from another command @@ -117,3 +130,8 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) { } return strings.TrimSpace(pass), nil } + +// PrintPrefixed prints a string with > prefixed for use in prompts. +func PrintPrefixed(msg string) { + fmt.Printf("> %s\n", msg) +} diff --git a/client/lcd/root.go b/client/lcd/root.go index 36fbd42d0b..84d3e806c9 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -121,6 +121,9 @@ 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)") + 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)) return cmd } diff --git a/client/rpc/block.go b/client/rpc/block.go index 3824bc3e5c..44def2d323 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -10,6 +10,7 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" + "github.com/spf13/viper" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" ) @@ -22,7 +23,9 @@ func BlockCommand() *cobra.Command { RunE: printBlock, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") return cmd } diff --git a/client/rpc/root.go b/client/rpc/root.go index a8171a293a..74c5a69a2e 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/spf13/viper" ) const ( @@ -40,6 +41,9 @@ func initClientCommand() *cobra.Command { cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") cmd.Flags().String(flagCommit, "", "File with trusted and signed header") cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) + return cmd } diff --git a/client/rpc/status.go b/client/rpc/status.go index ea090d32f9..6c42701754 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -10,6 +10,7 @@ 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" ) func statusCommand() *cobra.Command { @@ -20,6 +21,7 @@ func statusCommand() *cobra.Command { } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) return cmd } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 0583dc0545..d8572d5444 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" tmtypes "github.com/tendermint/tendermint/types" + "github.com/spf13/viper" ) // TODO these next two functions feel kinda hacky based on their placement @@ -26,8 +27,11 @@ func ValidatorCommand() *cobra.Command { RunE: printValidators, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) return cmd } diff --git a/client/tx/query.go b/client/tx/query.go index 732c11b659..f1d13b608a 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -3,8 +3,8 @@ package tx import ( "encoding/hex" "fmt" - "github.com/tendermint/tendermint/libs/common" "net/http" + "github.com/tendermint/tendermint/libs/common" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/spf13/viper" ) // QueryTxCmd implements the default command for a tx query. @@ -41,8 +42,11 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) return cmd } diff --git a/client/tx/search.go b/client/tx/search.go index 5bc8726db1..8d83c44b3e 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -62,8 +62,11 @@ $ gaiacli tendermint txs --tag test1,test2 --any } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 3c847dc90e..8e31c664bc 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "testing" + "path" "github.com/stretchr/testify/require" @@ -515,6 +516,48 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) } +func TestGaiaCLIConfig(t *testing.T) { + require.NoError(t, os.RemoveAll(gaiacliHome)) + require.NoError(t, os.RemoveAll(gaiadHome)) + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + node := fmt.Sprintf("%s:%s", servAddr, port) + chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y") + config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) + require.NoError(t, err) + expectedConfig := fmt.Sprintf(`chain_id = "%s" +encoding = "btc" +home = "%s" +node = "%s" +output = "text" +trace = false +trust_node = true +`, chainID, gaiacliHome, node) + require.Equal(t, expectedConfig, string(config)) + // ensure a backup gets created + executeWrite(t, "gaiacli config", gaiacliHome, node, "y", "y") + configBackup, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml-old")) + require.NoError(t, err) + require.Equal(t, expectedConfig, string(configBackup)) + + require.NoError(t, os.RemoveAll(gaiadHome)) + executeWrite(t, "gaiacli config", gaiacliHome, node, "y") + + // ensure it works without an initialized gaiad state + expectedConfig = fmt.Sprintf(`chain_id = "" +encoding = "btc" +home = "%s" +node = "%s" +output = "text" +trace = false +trust_node = true +`, gaiacliHome, node) + config, err = ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) + require.NoError(t, err) + require.Equal(t, expectedConfig, string(config)) +} + //___________________________________________________________________________________ // helper methods diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index d0e2182b93..392e22d127 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -19,6 +19,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" ) // rootCmd is the entry point for this binary @@ -36,6 +39,7 @@ func main() { // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc + rootCmd.AddCommand(client.ConfigCmd()) // add standard rpc commands rpc.AddCommands(rootCmd) @@ -146,9 +150,35 @@ func main() { // prepare and add flags executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome) - err := executor.Execute() + err := initConfig(rootCmd) + if err != nil { + panic(err) + } + + err = executor.Execute() if err != nil { // handle with #870 panic(err) } } + +func initConfig(cmd *cobra.Command) error { + home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) + if err != nil { + return err + } + + cfgFile := path.Join(home, "config", "config.toml") + if _, err := os.Stat(cfgFile); err == nil { + viper.SetConfigFile(cfgFile) + + if err := viper.ReadInConfig(); err != nil { + return err + } + } + + if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { + return err + } + return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) +} diff --git a/docs/spec/staking/hooks.md b/docs/spec/staking/hooks.md new file mode 100644 index 0000000000..bcc496e3d6 --- /dev/null +++ b/docs/spec/staking/hooks.md @@ -0,0 +1,19 @@ +## Receiver Hooks + +The staking module allow for the following hooks to be registered with staking events: + +``` golang +// event hooks for staking validator object +type StakingHooks interface { + OnValidatorCreated(ctx Context, address ValAddress) // called when a validator is created + OnValidatorCommissionChange(ctx Context, address ValAddress) // called when a validator's commission is modified + OnValidatorRemoved(ctx Context, address ValAddress) // called when a validator is deleted + + OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded + OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // called when a validator begins unbonding + + OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is created + OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation's shares are modified + OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is removed +} +``` diff --git a/types/stake.go b/types/stake.go index 5a8a4fd435..d70db1b56a 100644 --- a/types/stake.go +++ b/types/stake.go @@ -102,12 +102,25 @@ type DelegationSet interface { fn func(index int64, delegation Delegation) (stop bool)) } -// validator event hooks -// These can be utilized to communicate between a staking keeper -// and another keeper which must take particular actions when -// validators are bonded and unbonded. The second keeper must implement -// this interface, which then the staking keeper can call. -type ValidatorHooks interface { +//_______________________________________________________________________________ +// Event Hooks +// These can be utilized to communicate between a staking keeper and another +// keeper which must take particular actions when validators/delegators change +// state. The second keeper must implement this interface, which then the +// staking keeper can call. + +// TODO refactor event hooks out to the receiver modules + +// event hooks for staking validator object +type StakingHooks interface { + OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created + OnValidatorCommissionChange(ctx Context, address ValAddress) // Must be called when a validator's commission is modified + OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted + OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // Must be called when a validator begins unbonding + + OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is created + OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation's shares are modified + OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is removed } diff --git a/types/utils.go b/types/utils.go index 2e027676a0..95fc779d73 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,6 +1,10 @@ package types -import "encoding/json" +import ( + "encoding/json" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + tmtypes "github.com/tendermint/tendermint/types" +) // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. @@ -29,3 +33,22 @@ func MustSortJSON(toSortJSON []byte) []byte { } return js } + +// DefaultChainID returns the chain ID from the genesis file if present. An +// error is returned if the file cannot be read or parsed. +// +// TODO: This should be removed and the chainID should always be provided by +// the end user. +func DefaultChainID() (string, error) { + cfg, err := tcmd.ParseConfig() + if err != nil { + return "", err + } + + doc, err := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) + if err != nil { + return "", err + } + + return doc.ChainID, nil +} \ No newline at end of file diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 030ddb72a4..642893b52f 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -30,7 +30,7 @@ func NewTxBuilderFromCLI() TxBuilder { // if chain ID is not specified manually, read default chain ID chainID := viper.GetString(client.FlagChainID) if chainID == "" { - defaultChainID, err := defaultChainID() + defaultChainID, err := sdk.DefaultChainID() if err != nil { chainID = defaultChainID } diff --git a/x/auth/client/txbuilder/utils.go b/x/auth/client/txbuilder/utils.go deleted file mode 100644 index 22cc8220bb..0000000000 --- a/x/auth/client/txbuilder/utils.go +++ /dev/null @@ -1,25 +0,0 @@ -package context - -import ( - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - tmtypes "github.com/tendermint/tendermint/types" -) - -// defaultChainID returns the chain ID from the genesis file if present. An -// error is returned if the file cannot be read or parsed. -// -// TODO: This should be removed and the chainID should always be provided by -// the end user. -func defaultChainID() (string, error) { - cfg, err := tcmd.ParseConfig() - if err != nil { - return "", err - } - - doc, err := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) - if err != nil { - return "", err - } - - return doc.ChainID, nil -} diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index 92c4cf85f8..701a6b2cde 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -29,10 +29,10 @@ type Hooks struct { k Keeper } -var _ sdk.ValidatorHooks = Hooks{} +var _ sdk.StakingHooks = Hooks{} // Return the wrapper struct -func (k Keeper) ValidatorHooks() Hooks { +func (k Keeper) Hooks() Hooks { return Hooks{k} } @@ -45,3 +45,11 @@ func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { h.k.onValidatorBeginUnbonding(ctx, address) } + +// nolint - unused hooks +func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) OnValidatorCommissionChange(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) OnDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) OnDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) OnDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index d4b1af3d51..6845f35ce2 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -27,7 +27,7 @@ func TestHandleDoubleSign(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) - sk = sk.WithValidatorHooks(keeper.ValidatorHooks()) + sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt)) @@ -69,7 +69,7 @@ func TestSlashingPeriodCap(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) - sk = sk.WithValidatorHooks(keeper.ValidatorHooks()) + sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, amt := addrs[0], sdk.NewInt(amtInt) valConsPubKey, valConsAddr := pks[0], sdk.ConsAddress(pks[0].Address()) @@ -125,7 +125,7 @@ func TestHandleAbsentValidator(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) - sk = sk.WithValidatorHooks(keeper.ValidatorHooks()) + sk = sk.WithHooks(keeper.Hooks()) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) sh := stake.NewHandler(sk) diff --git a/x/stake/handler.go b/x/stake/handler.go index 0524b2c117..8b53065ccf 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -98,6 +98,10 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k return err.Result() } + k.OnValidatorCreated(ctx, validator.OperatorAddr) + accAddr := sdk.AccAddress(validator.OperatorAddr) + k.OnDelegationCreated(ctx, accAddr, validator.OperatorAddr) + tags := sdk.NewTags( tags.Action, tags.ActionCreateValidator, tags.DstValidator, []byte(msg.ValidatorAddr.String()), @@ -166,6 +170,9 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) return err.Result() } + // call the hook if present + k.OnDelegationCreated(ctx, msg.DelegatorAddr, validator.OperatorAddr) + tags := sdk.NewTags( tags.Action, tags.ActionDelegate, tags.Delegator, []byte(msg.DelegatorAddr.String()), diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 6efa4c8ed3..cc46646a73 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -66,7 +66,7 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { // remove a delegation from store func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { - + k.OnDelegationRemoved(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr) store := ctx.KVStore(k.storeKey) store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr)) } @@ -283,6 +283,8 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec) (amount sdk.Dec, err sdk.Error) { + k.OnDelegationSharesModified(ctx, delAddr, valAddr) + // check if delegation has any shares in it unbond delegation, found := k.GetDelegation(ctx, delAddr, valAddr) if !found { @@ -334,6 +336,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA k.RemoveValidator(ctx, validator.OperatorAddr) } + k.OnDelegationSharesModified(ctx, delegation.DelegatorAddr, validator.OperatorAddr) return amount, nil } diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 0f700f9ab8..82170a4aed 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -14,7 +14,7 @@ type Keeper struct { storeTKey sdk.StoreKey cdc *codec.Codec bankKeeper bank.Keeper - hooks sdk.ValidatorHooks + hooks sdk.StakingHooks // codespace codespace sdk.CodespaceType @@ -33,7 +33,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespa } // Set the validator hooks -func (k Keeper) WithValidatorHooks(sh sdk.ValidatorHooks) Keeper { +func (k Keeper) WithHooks(sh sdk.StakingHooks) Keeper { if k.hooks != nil { panic("cannot set validator hooks twice") } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 9d8fb4362a..84a82dba5f 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -617,12 +617,7 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat // also remove from the Bonded types.Validators Store store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) - // call the unbond hook if present - if k.hooks != nil { - k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) - } - - // return updated validator + k.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) return validator } @@ -652,18 +647,15 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. tstore := ctx.TransientStore(k.storeTKey) tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) - // call the bond hook if present - if k.hooks != nil { - k.hooks.OnValidatorBonded(ctx, validator.ConsAddress()) - } - - // return updated validator + 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 { @@ -703,6 +695,7 @@ func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Valid validator.Commission.UpdateTime = blockTime k.SetValidator(ctx, validator) + k.OnValidatorCommissionChange(ctx, validator.OperatorAddr) return nil }