Skip to content

Commit

Permalink
WIP adding unbonding/redelegation commands
Browse files Browse the repository at this point in the history
  • Loading branch information
rigelrozanski committed Jun 2, 2018
1 parent 300f0f0 commit 9619765
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 29 deletions.
1 change: 1 addition & 0 deletions cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func main() {
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
stakecmd.GetCmdRedelegate(cdc),
slashingcmd.GetCmdUnrevoke(cdc),
)...)
rootCmd.AddCommand(
Expand Down
14 changes: 8 additions & 6 deletions types/rational.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 }
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // greater than
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // less than
func (r Rat) Mul(r2 Rat) Rat { return Rat{*new(big.Rat).Mul(&(r.Rat), &(r2.Rat))} } // Mul - multiplication
func (r Rat) Quo(r2 Rat) Rat { return Rat{*new(big.Rat).Quo(&(r.Rat), &(r2.Rat))} } // Quo - quotient
func (r Rat) Add(r2 Rat) Rat { return Rat{*new(big.Rat).Add(&(r.Rat), &(r2.Rat))} } // Add - addition
func (r Rat) Sub(r2 Rat) Rat { return Rat{*new(big.Rat).Sub(&(r.Rat), &(r2.Rat))} } // Sub - subtraction
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // greater than
func (r Rat) GTE(r2 Rat) bool { return ((&(r.Rat)).Cmp(&(r2.Rat)) == 1 || r.Equal(r2)) } // greater than or equal
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // less than
func (r Rat) LTE(r2 Rat) bool { return ((&(r.Rat)).Cmp(&(r2.Rat)) == -1 || r.Equal(r2)) } // less than or equal
func (r Rat) Mul(r2 Rat) Rat { return Rat{*new(big.Rat).Mul(&(r.Rat), &(r2.Rat))} } // Mul - multiplication
func (r Rat) Quo(r2 Rat) Rat { return Rat{*new(big.Rat).Quo(&(r.Rat), &(r2.Rat))} } // Quo - quotient
func (r Rat) Add(r2 Rat) Rat { return Rat{*new(big.Rat).Add(&(r.Rat), &(r2.Rat))} } // Add - addition
func (r Rat) Sub(r2 Rat) Rat { return Rat{*new(big.Rat).Sub(&(r.Rat), &(r2.Rat))} } // Sub - subtraction
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) }

var (
Expand Down
31 changes: 19 additions & 12 deletions x/stake/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (

// nolint
const (
FlagAddressDelegator = "address-delegator"
FlagAddressValidator = "address-validator"
FlagPubKey = "pubkey"
FlagAmount = "amount"
FlagShares = "shares"
FlagAddressDelegator = "address-delegator"
FlagAddressValidator = "address-validator"
FlagAddressValidatorSrc = "addr-validator-source"
FlagAddressValidatorDst = "addr-validator-dest"
FlagPubKey = "pubkey"
FlagAmount = "amount"
FlagSharesAmount = "shares-amount"
FlagSharesPercent = "shares-percent"

FlagMoniker = "moniker"
FlagIdentity = "keybase-sig"
Expand All @@ -20,22 +23,26 @@ const (

// common flagsets to add to various functions
var (
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError)
)

func init() {
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220")
fsAmount.String(FlagAmount, "1steak", "Amount of coins to bond")
fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)")
fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal")
fsShares.String(FlagSharesPercent, "", "Percent of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1")
fsDescription.String(FlagMoniker, "", "validator name")
fsDescription.String(FlagIdentity, "", "optional keybase signature")
fsDescription.String(FlagWebsite, "", "optional website")
fsDescription.String(FlagDetails, "", "optional details")
fsValidator.String(FlagAddressValidator, "", "hex address of the validator")
fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator")
fsRedelegation.String(FlagAddressValidatorSrc, "", "hex address of the source validator")
fsRedelegation.String(FlagAddressValidatorDst, "", "hex address of the destination validator")
}
190 changes: 179 additions & 11 deletions x/stake/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
return cmd
}

// create edit validator command
// delegate command
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegate",
Short: "delegate coins to an existing validator",
Short: "delegate liquid tokens to an validator",
RunE: func(cmd *cobra.Command, args []string) error {
amount, err := sdk.ParseCoin(viper.GetString(FlagAmount))
if err != nil {
Expand Down Expand Up @@ -142,24 +142,160 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
return cmd
}

// create edit validator command
func GetCmdRedelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "redelegate",
Short: "redelegate illiquid tokens from one validator to another",
}
cmd.AddCommand(
GetCmdBeginRedelegate(cdc),
GetCmdCompleteRedelegate(cdc),
)
return cmd
}

// redelegate command
func GetCmdBeginRedelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "begin",
Short: "begin redelegation",
RunE: func(cmd *cobra.Command, args []string) error {

// check the shares before broadcasting
var sharesAmount, sharesPercent sdk.Rat
var err error
sharesAmountStr := viper.GetString(FlagSharesAmount)
sharesPercentStr := viper.GetString(FlagSharesPercent)
switch {
case sharesAmountStr != "" && sharesPercentStr != "":
return fmt.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr == "" && sharesPercentStr == "":
return fmt.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr != "":
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
if err != nil {
return err
}
if !sharesAmount.GT(sdk.ZeroRat()) {
return fmt.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
}
case sharesPercentStr != "":
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
if err != nil {
return err
}
if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) {
return fmt.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
}
}

delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}

msg := stake.NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr, sharesAmount, sharesPercent)

// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))

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

cmd.Flags().AddFlagSet(fsShares)
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsRedelegation)
return cmd
}

// redelegate command
func GetCmdCompleteRedelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete redelegation",
RunE: func(cmd *cobra.Command, args []string) error {

delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}

msg := stake.NewMsgCompleteRedelegation(delegatorAddr, validatorSrcAddr, validatorDstAddr)

// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))

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
},
}
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsRedelegation)
return cmd
}

// create edit validator command
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbond",
Short: "unbond shares from a validator",
Short: "begin or complete unbonding shares from a validator",
}
cmd.AddCommand(
GetCmdBeginUnbonding(cdc),
GetCmdCompleteUnbonding(cdc),
)
return cmd
}

// create edit validator command
func GetCmdBeginUnbonding(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "begin",
Short: "begin unbonding",
RunE: func(cmd *cobra.Command, args []string) error {

// check the shares before broadcasting
sharesStr := viper.GetString(FlagShares)
var shares sdk.Rat
if sharesStr != "MAX" {
var err error
shares, err = sdk.NewRatFromDecimal(sharesStr)
var sharesAmount, sharesPercent sdk.Rat
var err error
sharesAmountStr := viper.GetString(FlagSharesAmount)
sharesPercentStr := viper.GetString(FlagSharesPercent)
switch {
case sharesAmountStr != "" && sharesPercentStr != "":
return fmt.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr == "" && sharesPercentStr == "":
return fmt.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr != "":
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
if err != nil {
return err
}
if !sharesAmount.GT(sdk.ZeroRat()) {
return fmt.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
}
case sharesPercentStr != "":
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
if err != nil {
return err
}
if !shares.GT(sdk.ZeroRat()) {
return fmt.Errorf("shares must be positive integer or decimal (ex. 123, 1.23456789)")
if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) {
return fmt.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
}
}

Expand All @@ -169,7 +305,7 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
return err
}

msg := stake.NewMsgUnbond(delegatorAddr, validatorAddr, sharesStr)
msg := stake.NewMsgBeginUnbond(delegatorAddr, validatorAddr, sharesAmount, sharesPercent)

// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
Expand All @@ -189,3 +325,35 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
cmd.Flags().AddFlagSet(fsValidator)
return cmd
}

// create edit validator command
func GetCmdCompleteUnbonding(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete unbonding",
RunE: func(cmd *cobra.Command, args []string) error {

delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}

msg := stake.NewMsgCompleteUnbond(delegatorAddr, validatorAddr)

// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))

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
},
}
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsValidator)
return cmd
}
51 changes: 51 additions & 0 deletions x/stake/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,57 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error {

//______________________________________________________________________

// MsgDelegate - struct for bonding transactions
type MsgBeginRedelegate struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorSrcAddr sdk.Address `json:"validator_source_addr"`
ValidatorDstAddr sdk.Address `json:"validator_destination_addr"`
Shares sdk.Rat `json:"shares"`
}

func NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address, shares sdk.Rational) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delegatorAddr,
ValidatorSrcAddr: validatorSrcAddr,
ValidatorDstAddr: validatorDstAddr,
Shares: shares,
}
}

//nolint
func (msg MsgBeginRedelegate) Type() string { return MsgType }
func (msg MsgBeginRedelegate) GetSigners() []sdk.Address {
return []sdk.Address{msg.DelegatorAddr}
}

// get the bytes for the message signer to sign on
func (msg MsgBeginRedelegate) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return b
}

// quick validity check
func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrBadDelegatorAddr(DefaultCodespace)
}
if msg.ValidatorSrcAddr == nil {
return ErrBadValidatorAddr(DefaultCodespace)
}
if msg.ValidatorDstAddr == nil {
return ErrBadValidatorAddr(DefaultCodespace)
}
if msg.Bond.Amount <= 0 {
return ErrBadBondingAmount(DefaultCodespace)
}
return nil
}

//______________________________________________________________________

// MsgUnbond - struct for unbonding transactions
type MsgUnbond struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
Expand Down

0 comments on commit 9619765

Please sign in to comment.