diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 049c07b36..2e24842e3 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -14,6 +14,7 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -86,6 +87,7 @@ func main() { stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), + slashingcmd.GetCmdQuerySigningInfo("slashing", cdc), )...) stakeCmd.AddCommand( client.PostCommands( @@ -93,6 +95,7 @@ func main() { stakecmd.GetCmdEditValidator(cdc), stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond(cdc), + slashingcmd.GetCmdUnrevoke(cdc), )...) rootCmd.AddCommand( stakeCmd, diff --git a/x/slashing/client/cli/flags.go b/x/slashing/client/cli/flags.go new file mode 100644 index 000000000..43f8fa90a --- /dev/null +++ b/x/slashing/client/cli/flags.go @@ -0,0 +1,6 @@ +package cli + +// nolint +const ( + FlagAddressValidator = "address-validator" +) diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go new file mode 100644 index 000000000..42c02cab0 --- /dev/null +++ b/x/slashing/client/cli/query.go @@ -0,0 +1,57 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tmlibs/cli" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" // XXX fix + "github.com/cosmos/cosmos-sdk/x/slashing" +) + +// get the command to query signing info +func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "signing_info [validator-pubkey]", + Short: "Query a validator's signing information", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + + pk, err := sdk.GetValPubKeyBech32Cosmos(args[0]) + if err != nil { + return err + } + key := slashing.GetValidatorSigningInfoKey(pk.Address()) + ctx := context.NewCoreContextFromViper() + res, err := ctx.Query(key, storeName) + if err != nil { + return err + } + signingInfo := new(slashing.ValidatorSigningInfo) + cdc.MustUnmarshalBinary(res, signingInfo) + + switch viper.Get(cli.OutputFlag) { + + case "text": + human := signingInfo.HumanReadableString() + fmt.Println(human) + + case "json": + // parse out the signing info + output, err := wire.MarshalJSONIndent(cdc, signingInfo) + if err != nil { + return err + } + fmt.Println(string(output)) + } + + return nil + }, + } + + return cmd +} diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go new file mode 100644 index 000000000..13a5cb666 --- /dev/null +++ b/x/slashing/client/cli/tx.go @@ -0,0 +1,42 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/slashing" +) + +// create unrevoke command +func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "unrevoke", + Args: cobra.ExactArgs(1), + Short: "unrevoke validator previously revoked for downtime", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc)) + + validatorAddr, err := sdk.GetAccAddressBech32Cosmos(args[0]) + if err != nil { + return err + } + + msg := slashing.NewMsgUnrevoke(validatorAddr) + + // build and sign the transaction, then broadcast to Tendermint + res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc) + if err != nil { + return err + } + + fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String()) + return nil + }, + } + return cmd +} diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 05285b680..a2df0505a 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -2,14 +2,15 @@ package slashing import ( "encoding/binary" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" ) // Stored by *validator* address (not owner address) -func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.Address) (info validatorSigningInfo, found bool) { +func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.Address) (info ValidatorSigningInfo, found bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(validatorSigningInfoKey(address)) + bz := store.Get(GetValidatorSigningInfoKey(address)) if bz == nil { found = false return @@ -20,16 +21,16 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.Address) (i } // Stored by *validator* address (not owner address) -func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.Address, info validatorSigningInfo) { +func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.Address, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinary(info) - store.Set(validatorSigningInfoKey(address), bz) + store.Set(GetValidatorSigningInfoKey(address), bz) } // Stored by *validator* address (not owner address) func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.Address, index int64) (signed bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(validatorSigningBitArrayKey(address, index)) + bz := store.Get(GetValidatorSigningBitArrayKey(address, index)) if bz == nil { // lazy: treat empty key as unsigned signed = false @@ -43,23 +44,30 @@ func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.Address func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.Address, index int64, signed bool) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinary(signed) - store.Set(validatorSigningBitArrayKey(address, index), bz) + store.Set(GetValidatorSigningBitArrayKey(address, index), bz) } -type validatorSigningInfo struct { +// Signing info for a validator +type ValidatorSigningInfo struct { StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array JailedUntil int64 `json:"jailed_until"` // timestamp validator cannot be unrevoked until SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time) } +// Return human readable signing info +func (i ValidatorSigningInfo) HumanReadableString() string { + return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %d, signed blocks counter: %d", + i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter) +} + // Stored by *validator* address (not owner address) -func validatorSigningInfoKey(v sdk.Address) []byte { +func GetValidatorSigningInfoKey(v sdk.Address) []byte { return append([]byte{0x01}, v.Bytes()...) } // Stored by *validator* address (not owner address) -func validatorSigningBitArrayKey(v sdk.Address, i int64) []byte { +func GetValidatorSigningBitArrayKey(v sdk.Address, i int64) []byte { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(i)) return append([]byte{0x02}, append(v.Bytes(), b...)...) diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index 8000d6745..26113e715 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -10,7 +10,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { ctx, _, _, keeper := createTestInput(t) info, found := keeper.getValidatorSigningInfo(ctx, addrs[0]) require.False(t, found) - newInfo := validatorSigningInfo{ + newInfo := ValidatorSigningInfo{ StartHeight: int64(4), IndexOffset: int64(3), JailedUntil: int64(2),