Merge PR #3857: Remove Shares Concept from Unbond/Redelegate UX

* Remove shares concept from unbonding and redelegation

* Remove redundant staking REST type declerations

* Rename staking REST request types

* Fix slashing tests

* Fix staking tests

* Fix integration tests

* Add safety checks for when validator tokens are zero

* Attempt to fix simulation

* Add pending log entry

* Update docs

* Implement and use SharesFromTokens

* Rename ShareTokens and ShareTokensTruncated

* Rename Delegation to Amount in DelegateRequest

* Implement and use SharesFromTokensTruncated

* Update MsgDelegate to use Amount instead of Value

* Use constructors in staking sim messages

* Implement and use ValidateUnbondAmount
This commit is contained in:
Alexander Bezobchuk 2019-03-25 17:13:02 -04:00 committed by frog power 4000
parent 2788c2250d
commit 59765cecb1
24 changed files with 298 additions and 218 deletions

View File

@ -0,0 +1,2 @@
#3516 Remove concept of shares from staking unbonding and redelegation UX;
replaced by direct coin amount.

View File

@ -533,7 +533,7 @@ func TestBonding(t *testing.T) {
// this test takes a long time to run, eventually this second validator
// will get slashed, meaning that it's exchange rate is no-longer 1-to-1,
// hence we utilize the exchange rate in the following test
delTokensAfterRedelegation := validator2.ShareTokens(delegatorDels[0].GetShares())
delTokensAfterRedelegation := validator2.TokensFromShares(delegatorDels[0].GetShares())
require.Equal(t, rdTokens.ToDec(), delTokensAfterRedelegation)
// verify balance after paying fees

View File

@ -815,11 +815,11 @@ func doDelegate(
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgDelegationsInput{
msg := stakingrest.DelegateRequest{
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
Delegation: sdk.NewCoin(sdk.DefaultBondDenom, amount),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount),
}
req, err := cdc.MarshalJSON(msg)
@ -839,13 +839,6 @@ func doDelegate(
return txResp
}
type msgDelegationsInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
Delegation sdk.Coin `json:"delegation"`
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doUndelegate(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress,
@ -859,11 +852,11 @@ func doUndelegate(
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgUndelegateInput{
msg := stakingrest.UndelegateRequest{
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
SharesAmount: amount.ToDec(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount),
}
req, err := cdc.MarshalJSON(msg)
@ -882,13 +875,6 @@ func doUndelegate(
return txResp
}
type msgUndelegateInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doBeginRedelegation(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress, valSrcAddr,
@ -902,12 +888,12 @@ func doBeginRedelegation(
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := stakingrest.MsgBeginRedelegateInput{
msg := stakingrest.RedelegateRequest{
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorSrcAddress: valSrcAddr,
ValidatorDstAddress: valDstAddr,
SharesAmount: amount.ToDec(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, amount),
}
req, err := cdc.MarshalJSON(msg)
@ -926,14 +912,6 @@ func doBeginRedelegation(
return txResp
}
type msgBeginRedelegateInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` // in bech32
ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
}
// GET /staking/delegators/{delegatorAddr}/delegations Get all delegations from a delegator
func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []staking.Delegation {
res, body := Request(t, port, "GET", fmt.Sprintf("/staking/delegators/%s/delegations", delegatorAddr), nil)

View File

@ -394,13 +394,13 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.NotZero(t, validatorDelegations[0].Shares)
// unbond a single share
unbondTokens := sdk.TokensFromTendermintPower(1)
success = f.TxStakingUnbond(keyBar, unbondTokens.String(), barVal, "-y")
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(1))
success = f.TxStakingUnbond(keyBar, unbondAmt.String(), barVal, "-y")
require.True(t, success)
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure bonded staking is correct
remainingTokens := newValTokens.Sub(unbondTokens)
remainingTokens := newValTokens.Sub(unbondAmt.Amount)
validator = f.QueryStakingValidator(barVal)
require.Equal(t, remainingTokens, validator.Tokens)

View File

@ -76,7 +76,7 @@ When a validator wishes to withdraw their transaction fees it must send
triggered each with any change in individual delegations, such as an unbond,
redelegation, or delegation of additional tokens to a specific validator. This
transaction withdraws the validators commission rewards, as well as any rewards
earning on their self-delegation.
earning on their self-delegation.
```golang
type TxWithdrawValidator struct {

View File

@ -376,12 +376,13 @@ gaiacli query staking delegations <delegator_addr>
#### Unbond Tokens
If for any reason the validator misbehaves, or you just want to unbond a certain amount of tokens, use this following command. You can unbond a specific `shares-amount` (eg:`12.1`\) or a `shares-fraction` (eg:`0.25`) with the corresponding flags.
If for any reason the validator misbehaves, or you just want to unbond a certain
amount of tokens, use this following command.
```bash
gaiacli tx staking unbond \
--validator=<account_cosmosval> \
--shares-fraction=0.5 \
<validator_addr> \
10atom \
--from=<key_name> \
--chain-id=<chain_id>
```
@ -414,9 +415,9 @@ A redelegation is a type delegation that allows you to bond illiquid tokens from
```bash
gaiacli tx staking redelegate \
--addr-validator-source=<account_cosmosval> \
--addr-validator-dest=<account_cosmosval> \
--shares-fraction=50 \
<src-validator-operator-addr> \
<dst-validator-operator-addr> \
10atom \
--from=<key_name> \
--chain-id=<chain_id>
```

View File

@ -375,13 +375,12 @@ gaiacli query staking delegation <delegator_addr(위임자 코스모스 주소)>
만약 특정 검증인이 악의적인 행동을 했거나 또는 본인이 개인적인 이유로 일부 토큰을 언본딩을 워하는 경우 다음 명령어를 통해 토큰을 언본딩 할 수 있습니다. 언본딩은 정확한 수량인 `shares-amount`(예시, `12.1`) 또는 언본딩을 원하는 물량의 비율인 `shares-fraction`(예시, `0.25`) 값으로 표현될 수 있습니다.
```bash
gaiacli tx staking unbond \
--validator=<account_cosmosval(검증인 cosmosval 주소)> \
--shares-fraction=0.5 \
--from=<key_name(트랜잭션을 발생시킬 /계정 이름)> \
--chain-id=<chain_id(체인 아이디)>
<validator_addr> \
10atom \
--from=<key_name> \
--chain-id=<chain_id>
```
언본딩은 언본딩 기간이 끝나는 대로 완료됩니다.
@ -412,11 +411,11 @@ gaiacli query staking unbonding-delegations-from <account_cosmosval(검증인 co
```bash
gaiacli tx staking redelegate \
--addr-validator-source=<account_cosmosval(스테이킹을 취소할 검증인의 cosmosval 주소)> \
--addr-validator-dest=<account_cosmosval(스테이킹을 받을 검증인의 cosmosval 주소)> \
--shares-fraction=50 \
--from=<key_name(트랜잭션을 발생시킬 /계정 이름)> \
--chain-id=<chain_id(체인 아이디)>
<src-validator-operator-addr> \
<dst-validator-operator-addr> \
10atom \
--from=<key_name> \
--chain-id=<chain_id>
```
위 예시와 같이 재위임될 토큰의 수량은 특정 수량(`shares-amount`) 또는 일정 비율(`shares-fraction`)로 표현될 수 있습니다.

View File

@ -61,20 +61,22 @@ func (b BondStatus) Equal(b2 BondStatus) bool {
// validator for a delegated proof of stake system
type Validator interface {
GetJailed() bool // whether the validator is jailed
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator
GetOperator() ValAddress // operator address to receive/return validators coins
GetConsPubKey() crypto.PubKey // validation consensus pubkey
GetConsAddr() ConsAddress // validation consensus address
GetTokens() Int // validation tokens
GetBondedTokens() Int // validator bonded tokens
GetTendermintPower() int64 // validation power in tendermint
GetCommission() Dec // validator commission rate
GetMinSelfDelegation() Int // validator minimum self delegation
GetDelegatorShares() Dec // total outstanding delegator shares
ShareTokens(Dec) Dec // token worth of provided delegator shares
ShareTokensTruncated(Dec) Dec // token worth of provided delegator shares, truncated
GetJailed() bool // whether the validator is jailed
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator
GetOperator() ValAddress // operator address to receive/return validators coins
GetConsPubKey() crypto.PubKey // validation consensus pubkey
GetConsAddr() ConsAddress // validation consensus address
GetTokens() Int // validation tokens
GetBondedTokens() Int // validator bonded tokens
GetTendermintPower() int64 // validation power in tendermint
GetCommission() Dec // validator commission rate
GetMinSelfDelegation() Int // validator minimum self delegation
GetDelegatorShares() Dec // total outstanding delegator shares
TokensFromShares(Dec) Dec // token worth of provided delegator shares
TokensFromSharesTruncated(Dec) Dec // token worth of provided delegator shares, truncated
SharesFromTokens(amt Int) (Dec, Error) // shares worth of delegator's bond
SharesFromTokensTruncated(amt Int) (Dec, Error) // truncated shares worth of delegator's bond
}
// validator which fulfills abci validator interface for use in Tendermint

View File

@ -22,7 +22,7 @@ func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sd
// calculate delegation stake in tokens
// we don't store directly, so multiply delegation shares * (tokens per share)
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake := validator.ShareTokensTruncated(delegation.GetShares())
stake := validator.TokensFromSharesTruncated(delegation.GetShares())
k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
}
@ -90,7 +90,7 @@ func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val sdk.Validator, d
// a stake sanity check - recalculated final stake should be less than or equal to current stake
// here we cannot use Equals because stake is truncated when multiplied by slash fractions
// we could only use equals if we had arbitrary-precision rationals
currentStake := val.ShareTokens(del.GetShares())
currentStake := val.TokensFromShares(del.GetShares())
if stake.GT(currentStake) {
panic(fmt.Sprintf("calculated final stake for delegator %s greater than current stake: %s, %s",
del.GetDelegatorAddr(), stake, currentStake))

View File

@ -31,7 +31,7 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
return ErrMissingSelfDelegation(k.codespace).Result()
}
if validator.ShareTokens(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
return ErrSelfDelegationTooLowToUnjail(k.codespace).Result()
}

View File

@ -51,7 +51,8 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
)
undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, sdk.OneDec())
unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt())
undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt)
got = staking.NewHandler(sk)(ctx, undelegateMsg)
require.True(t, sk.Validator(ctx, addr).GetJailed())
@ -92,10 +93,10 @@ func TestJailedValidatorDelegations(t *testing.T) {
got = staking.NewHandler(stakingKeeper)(ctx, msgDelegate)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
unbondShares := bondAmount.ToDec()
unbondAmt := sdk.NewCoin(stakingKeeper.GetParams(ctx).BondDenom, bondAmount)
// unbond validator total self-delegations (which should jail the validator)
msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondShares)
msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt)
got = staking.NewHandler(stakingKeeper)(ctx, msgUndelegate)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got: %v", got)

View File

@ -73,7 +73,10 @@ func TestHandleDoubleSign(t *testing.T) {
// Should be able to unbond now
del, _ := sk.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr)
msgUnbond := staking.NewMsgUndelegate(sdk.AccAddress(operatorAddr), operatorAddr, del.GetShares())
validator, _ := sk.GetValidator(ctx, operatorAddr)
totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt()
msgUnbond := staking.NewMsgUndelegate(sdk.AccAddress(operatorAddr), operatorAddr, sdk.NewCoin(sk.GetParams(ctx).BondDenom, totalBond))
res = staking.NewHandler(sk)(ctx, msgUnbond)
require.True(t, res.IsOK())
}

View File

@ -157,7 +157,7 @@ func TestStakingMsgs(t *testing.T) {
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, bondTokens.ToDec())
// begin unbonding
beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondTokens.ToDec())
beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondCoin)
header = abci.Header{Height: mApp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, []uint64{1}, []uint64{1}, true, true, priv2)

View File

@ -153,7 +153,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
Args: cobra.ExactArgs(3),
Long: strings.TrimSpace(`Redelegate an amount of illiquid staking tokens from one validator to another:
$ gaiacli tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100 --from mykey
$ gaiacli tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc))
@ -161,8 +161,6 @@ $ gaiacli tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmq
WithCodec(cdc).
WithAccountDecoder(cdc)
// var err error
delAddr := cliCtx.GetFromAddress()
valSrcAddr, err := sdk.ValAddressFromBech32(args[0])
if err != nil {
@ -174,13 +172,12 @@ $ gaiacli tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmq
return err
}
// get the shares amount
sharesAmount, err := getShares(args[2], delAddr, valSrcAddr)
amount, err := sdk.ParseCoin(args[2])
if err != nil {
return err
}
msg := staking.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, sharesAmount)
msg := staking.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
},
}
@ -194,7 +191,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command {
Args: cobra.ExactArgs(2),
Long: strings.TrimSpace(`Unbond an amount of bonded shares from a validator:
$ gaiacli tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100 --from mykey
$ gaiacli tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc))
@ -208,13 +205,12 @@ $ gaiacli tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
return err
}
// get the shares amount
sharesAmount, err := getShares(args[1], delAddr, valAddr)
amount, err := sdk.ParseCoin(args[1])
if err != nil {
return err
}
msg := staking.NewMsgUndelegate(delAddr, valAddr, sharesAmount)
msg := staking.NewMsgUndelegate(delAddr, valAddr, amount)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
},
}

View File

@ -7,19 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func getShares(sharesAmountStr string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sharesAmount sdk.Dec, err error) {
sharesAmount, err = sdk.NewDecFromStr(sharesAmountStr)
if err != nil {
return sharesAmount, err
}
if !sharesAmount.GT(sdk.ZeroDec()) {
return sharesAmount, errors.New("shares amount must be positive number (ex. 123, 1.23456789)")
}
return
}
func buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr string) (commission types.CommissionMsg, err error) {
if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" {
return commission, errors.New("must specify all validator commission parameters")

View File

@ -31,35 +31,35 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec
}
type (
// MsgBeginRedelegateInput defines the properties of a delegation request's body.
MsgDelegationsInput struct {
// DelegateRequest defines the properties of a delegation request's body.
DelegateRequest struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
Delegation sdk.Coin `json:"delegation"`
Amount sdk.Coin `json:"amount"`
}
// MsgBeginRedelegateInput defines the properties of a redelegate request's body.
MsgBeginRedelegateInput struct {
// RedelegateRequest defines the properties of a redelegate request's body.
RedelegateRequest struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` // in bech32
ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
Amount sdk.Coin `json:"amount"`
}
// MsgUndelegateInput defines the properties of a undelegate request's body.
MsgUndelegateInput struct {
// UndelegateRequest defines the properties of a undelegate request's body.
UndelegateRequest struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
Amount sdk.Coin `json:"amount"`
}
)
func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req MsgDelegationsInput
var req DelegateRequest
if !rest.ReadRESTReq(w, r, cdc, &req) {
return
@ -70,7 +70,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.
return
}
msg := staking.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Delegation)
msg := staking.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
@ -93,7 +93,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.
func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req MsgBeginRedelegateInput
var req RedelegateRequest
if !rest.ReadRESTReq(w, r, cdc, &req) {
return
@ -104,7 +104,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex
return
}
msg := staking.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.SharesAmount)
msg := staking.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
@ -127,7 +127,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex
func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req MsgUndelegateInput
var req UndelegateRequest
if !rest.ReadRESTReq(w, r, cdc, &req) {
return
@ -138,7 +138,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx
return
}
msg := staking.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.SharesAmount)
msg := staking.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return

View File

@ -209,11 +209,11 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
return ErrNoValidatorFound(k.Codespace()).Result()
}
if msg.Value.Denom != k.GetParams(ctx).BondDenom {
if msg.Amount.Denom != k.GetParams(ctx).BondDenom {
return ErrBadDenom(k.Codespace()).Result()
}
_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Value.Amount, validator, true)
_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, validator, true)
if err != nil {
return err.Result()
}
@ -229,7 +229,14 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
}
func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) sdk.Result {
completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.SharesAmount)
shares, err := k.ValidateUnbondAmount(
ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.Amount.Amount,
)
if err != nil {
return err.Result()
}
completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, shares)
if err != nil {
return err.Result()
}
@ -245,8 +252,16 @@ func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keep
}
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
completionTime, err := k.BeginRedelegation(ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress,
msg.ValidatorDstAddress, msg.SharesAmount)
shares, err := k.ValidateUnbondAmount(
ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.Amount.Amount,
)
if err != nil {
return err.Result()
}
completionTime, err := k.BeginRedelegation(
ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress, shares,
)
if err != nil {
return err.Result()
}

View File

@ -71,6 +71,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
keeper.Slash(ctx, consAddr0, 0, initPower, 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
@ -91,14 +92,18 @@ func TestValidatorByPowerIndex(t *testing.T) {
require.Equal(t, power2, power3)
// unbond self-delegation
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, initBond.ToDec())
totalBond := validator.TokensFromShares(bond.GetShares()).TruncateInt()
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, totalBond)
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
EndBlocker(ctx, keeper)
// verify that by power key nolonger exists
@ -215,8 +220,8 @@ func TestLegacyValidatorDelegations(t *testing.T) {
require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens())
// unbond validator total self-delegations (which should jail the validator)
unbondShares := sdk.TokensFromTendermintPower(10)
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondShares.ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, bondAmount)
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got)
@ -437,8 +442,8 @@ func TestIncrementsMsgUnbond(t *testing.T) {
// just send the same msgUnbond multiple times
// TODO use decimals here
unbondShares := sdk.NewDec(10)
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares)
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
numUnbonds := int64(5)
for i := int64(0); i < numUnbonds; i++ {
@ -455,8 +460,8 @@ func TestIncrementsMsgUnbond(t *testing.T) {
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
require.True(t, found)
expBond := initBond.Sub(unbondShares.MulInt64(i + 1).RoundInt())
expDelegatorShares := (initBond.MulRaw(2)).Sub(unbondShares.MulInt64(i + 1).RoundInt())
expBond := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1)))
expDelegatorShares := initBond.MulRaw(2).Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1)))
expDelegatorAcc := initBond.Sub(expBond)
gotBond := bond.Shares.RoundInt()
@ -482,28 +487,22 @@ func TestIncrementsMsgUnbond(t *testing.T) {
sdk.TokensFromTendermintPower(1 << 31),
initBond,
}
for i, c := range errorCases {
unbondShares := c.ToDec()
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares)
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, c)
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.False(t, got.IsOK(), "expected unbond msg to fail, index: %v", i)
}
leftBonded := initBond.Sub(unbondShares.MulInt64(numUnbonds).RoundInt())
leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds)))
// should be unable to unbond one more than we have
unbondShares = leftBonded.AddRaw(1).ToDec()
msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.False(t, got.IsOK(),
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUndelegate, unbondShares.String(), leftBonded)
// should be able to unbond just what we have
unbondShares = leftBonded.ToDec()
msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares)
// should be able to unbond remaining
unbondAmt = sdk.NewCoin(sdk.DefaultBondDenom, leftBonded)
msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(),
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUndelegate, unbondShares, leftBonded)
"got: %v\nmsgUnbond: %v\nshares: %s\nleftBonded: %s\n", got.Log, msgUndelegate, unbondAmt, leftBonded)
}
func TestMultipleMsgCreateValidator(t *testing.T) {
@ -548,14 +547,18 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
for i, validatorAddr := range validatorAddrs {
_, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
unbondingTokens := sdk.TokensFromTendermintPower(10)
msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, unbondingTokens.ToDec()) // remove delegation
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(10))
msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, unbondAmt) // remove delegation
got := handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
// Jump to finishTime for unbonding period and remove from unbonding queue
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
// Check that the validator is deleted from state
@ -576,7 +579,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
validatorAddr, delegatorAddrs := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1:]
_ = setInstantUnbondPeriod(keeper, ctx)
//first make a validator
// first make a validator
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10))
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
@ -587,7 +590,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
got := handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is bonded
// check that the account is bonded
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
require.True(t, found)
require.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
@ -595,15 +598,19 @@ func TestMultipleMsgDelegate(t *testing.T) {
// unbond them all
for i, delegatorAddr := range delegatorAddrs {
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, sdk.NewDec(10))
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
got := handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
//Check that the account is unbonded
// check that the account is unbonded
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
require.False(t, found)
}
@ -625,11 +632,14 @@ func TestJailValidator(t *testing.T) {
require.True(t, got.IsOK(), "expected ok, got %v", got)
// unbond the validators bond portion
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper)
require.True(t, got.IsOK(), "expected no error: %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
@ -638,10 +648,12 @@ func TestJailValidator(t *testing.T) {
require.True(t, validator.Jailed, "%v", validator)
// test that the delegator can still withdraw their bonds
msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, sdk.NewDec(10))
msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
@ -674,14 +686,17 @@ func TestValidatorQueue(t *testing.T) {
EndBlocker(ctx, keeper)
// unbond the all self-delegation to put validator in unbonding state
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr),
validatorAddr, delTokens.ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, delTokens)
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper)
require.True(t, got.IsOK(), "expected no error: %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
origHeader := ctx.BlockHeader()
validator, found := keeper.GetValidator(ctx, validatorAddr)
@ -691,6 +706,7 @@ func TestValidatorQueue(t *testing.T) {
// should still be unbonding at time 6 seconds later
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6))
EndBlocker(ctx, keeper)
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.True(t, validator.GetStatus() == sdk.Unbonding, "%v", validator)
@ -698,6 +714,7 @@ func TestValidatorQueue(t *testing.T) {
// should be in unbonded state at time 7 seconds later
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7))
EndBlocker(ctx, keeper)
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.True(t, validator.GetStatus() == sdk.Unbonded, "%v", validator)
@ -721,11 +738,11 @@ func TestUnbondingPeriod(t *testing.T) {
EndBlocker(ctx, keeper)
// begin unbonding
unbondingTokens := sdk.TokensFromTendermintPower(10)
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr),
validatorAddr, unbondingTokens.ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(10))
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
origHeader := ctx.BlockHeader()
_, found := keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
@ -764,7 +781,8 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) {
require.True(t, got.IsOK(), "expected ok, got %v", got)
// unbond the validators bond portion
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
@ -774,7 +792,7 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) {
ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1))
// unbond the delegator from the validator
msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, sdk.NewDec(10))
msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
@ -820,7 +838,8 @@ func TestRedelegationPeriod(t *testing.T) {
bal1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins()
// begin redelegate
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, sdk.NewDec(10))
redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
@ -873,12 +892,13 @@ func TestTransitiveRedelegation(t *testing.T) {
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// begin redelegate
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, sdk.NewDec(10))
redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
// cannot redelegation to next validator while first delegation exists
msgBeginRedelegate = NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr2, validatorAddr3, sdk.NewDec(10))
msgBeginRedelegate = NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr2, validatorAddr3, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
@ -915,8 +935,8 @@ func TestMultipleRedelegationAtSameTime(t *testing.T) {
// begin a redelegate
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr,
valAddr, valAddr2, valTokens.QuoRaw(2).ToDec())
redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2))
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
@ -967,8 +987,8 @@ func TestMultipleRedelegationAtUniqueTimes(t *testing.T) {
// begin a redelegate
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr,
valAddr, valAddr2, valTokens.QuoRaw(2).ToDec())
redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2))
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
@ -1016,7 +1036,8 @@ func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) {
// begin an unbonding delegation
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2).ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2))
msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
@ -1062,7 +1083,8 @@ func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) {
// begin an unbonding delegation
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2).ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2))
msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
@ -1132,8 +1154,9 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetLastValidators(ctx)))
// unbond the valdator-2
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, valTokens2.ToDec())
// unbond the validator-2
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens2)
msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgUndelegate")
@ -1177,21 +1200,21 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ctx = ctx.WithBlockHeight(1)
// begin unbonding 4 stake
ubdTokens := sdk.TokensFromTendermintPower(4)
msgUndelegate := NewMsgUndelegate(del, valA, ubdTokens.ToDec())
unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(4))
msgUndelegate := NewMsgUndelegate(del, valA, unbondAmt)
got = handleMsgUndelegate(ctx, msgUndelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgUndelegate")
// begin redelegate 6 stake
rdTokens := sdk.TokensFromTendermintPower(6)
msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, rdTokens.ToDec())
redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(6))
msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, redAmt)
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginRedelegate")
// destination delegation should have 6 shares
delegation, found := keeper.GetDelegation(ctx, del, valB)
require.True(t, found)
require.Equal(t, rdTokens.ToDec(), delegation.Shares)
require.Equal(t, sdk.NewDecFromInt(redAmt.Amount), delegation.Shares)
// must apply validator updates
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
@ -1204,7 +1227,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ubd, found := keeper.GetUnbondingDelegation(ctx, del, valA)
require.True(t, found)
require.Len(t, ubd.Entries, 1)
require.Equal(t, ubdTokens.QuoRaw(2), ubd.Entries[0].Balance)
require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance)
// redelegation should have been slashed by half
redelegation, found := keeper.GetRedelegation(ctx, del, valA, valB)
@ -1214,7 +1237,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
// destination delegation should have been slashed by half
delegation, found = keeper.GetDelegation(ctx, del, valB)
require.True(t, found)
require.Equal(t, rdTokens.QuoRaw(2).ToDec(), delegation.Shares)
require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares)
// validator power should have been reduced by half
validator, found := keeper.GetValidator(ctx, valA)
@ -1229,7 +1252,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ubd, found = keeper.GetUnbondingDelegation(ctx, del, valA)
require.True(t, found)
require.Len(t, ubd.Entries, 1)
require.Equal(t, ubdTokens.QuoRaw(2), ubd.Entries[0].Balance)
require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance)
// redelegation should be unchanged
redelegation, found = keeper.GetRedelegation(ctx, del, valA, valB)
@ -1239,7 +1262,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
// destination delegation should be unchanged
delegation, found = keeper.GetDelegation(ctx, del, valB)
require.True(t, found)
require.Equal(t, rdTokens.QuoRaw(2).ToDec(), delegation.Shares)
require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares)
// end blocker
EndBlocker(ctx, keeper)

View File

@ -518,7 +518,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum
// trigger a jail validator
if isValidatorOperator && !validator.Jailed &&
validator.ShareTokens(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {
validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {
k.jailValidator(ctx, validator)
validator = k.mustGetValidator(ctx, validator.OperatorAddress)
@ -726,3 +726,46 @@ func (k Keeper) CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
return nil
}
// ValidateUnbondAmount validates that a given unbond or redelegation amount is
// valied based on upon the converted shares. If the amount is valid, the total
// amount of respective shares is returned, otherwise an error is returned.
func (k Keeper) ValidateUnbondAmount(
ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int,
) (shares sdk.Dec, err sdk.Error) {
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return shares, types.ErrNoValidatorFound(k.Codespace())
}
del, found := k.GetDelegation(ctx, delAddr, valAddr)
if !found {
return shares, types.ErrNoDelegation(k.Codespace())
}
shares, err = validator.SharesFromTokens(amt)
if err != nil {
return shares, err
}
sharesTruncated, err := validator.SharesFromTokensTruncated(amt)
if err != nil {
return shares, err
}
delShares := del.GetShares()
if sharesTruncated.GT(delShares) {
return shares, types.ErrBadSharesAmount(k.Codespace())
}
// Cap the shares at the delegation's shares. Shares being greater could occur
// due to rounding, however we don't want to truncate the shares or take the
// minimum because we want to allow for the full withdraw of shares from a
// delegation.
if shares.GT(delShares) {
shares = delShares
}
return shares, nil
}

View File

@ -148,24 +148,31 @@ func SimulateMsgUndelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Op
}
delegation := delegations[r.Intn(len(delegations))]
numShares := simulation.RandomDecAmount(r, delegation.Shares)
if numShares.Equal(sdk.ZeroDec()) {
validator, found := k.GetValidator(ctx, delegation.GetValidatorAddr())
if !found {
return simulation.NoOpMsg(), nil, nil
}
msg := staking.MsgUndelegate{
DelegatorAddress: delegatorAddress,
ValidatorAddress: delegation.ValidatorAddress,
SharesAmount: numShares,
totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt()
unbondAmt := simulation.RandomAmount(r, totalBond)
if unbondAmt.Equal(sdk.ZeroInt()) {
return simulation.NoOpMsg(), nil, nil
}
msg := staking.NewMsgDelegate(
delegatorAddress, delegation.ValidatorAddress, sdk.NewCoin(k.GetParams(ctx).BondDenom, unbondAmt),
)
if msg.ValidateBasic() != nil {
return simulation.NoOpMsg(), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s, got error %v",
msg.GetSignBytes(), msg.ValidateBasic())
}
ctx, write := ctx.CacheContext()
ok := handler(ctx, msg).IsOK()
if ok {
write()
}
opMsg = simulation.NewOperationMsg(msg, ok, "")
return opMsg, nil, nil
}
@ -195,20 +202,20 @@ func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k staking.Keeper) simulati
if amount.Equal(sdk.ZeroInt()) {
return simulation.NoOpMsg(), nil, nil
}
msg := staking.MsgBeginRedelegate{
DelegatorAddress: delegatorAddress,
ValidatorSrcAddress: srcValidatorAddress,
ValidatorDstAddress: destValidatorAddress,
SharesAmount: amount.ToDec(),
}
msg := staking.NewMsgBeginRedelegate(
delegatorAddress, srcValidatorAddress, destValidatorAddress, sdk.NewCoin(denom, amount),
)
if msg.ValidateBasic() != nil {
return simulation.NoOpMsg(), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
ok := handler(ctx, msg).IsOK()
if ok {
write()
}
opMsg = simulation.NewOperationMsg(msg, ok, "")
return opMsg, nil, nil
}

View File

@ -212,14 +212,14 @@ func (msg MsgEditValidator) ValidateBasic() sdk.Error {
type MsgDelegate struct {
DelegatorAddress sdk.AccAddress `json:"delegator_address"`
ValidatorAddress sdk.ValAddress `json:"validator_address"`
Value sdk.Coin `json:"value"`
Amount sdk.Coin `json:"amount"`
}
func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, value sdk.Coin) MsgDelegate {
func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgDelegate {
return MsgDelegate{
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
Value: value,
Amount: amount,
}
}
@ -244,7 +244,7 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error {
if msg.ValidatorAddress.Empty() {
return ErrNilValidatorAddr(DefaultCodespace)
}
if msg.Value.Amount.LTE(sdk.ZeroInt()) {
if msg.Amount.Amount.LTE(sdk.ZeroInt()) {
return ErrBadDelegationAmount(DefaultCodespace)
}
return nil
@ -257,17 +257,17 @@ type MsgBeginRedelegate struct {
DelegatorAddress sdk.AccAddress `json:"delegator_address"`
ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"`
ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"`
SharesAmount sdk.Dec `json:"shares_amount"`
Amount sdk.Coin `json:"amount"`
}
func NewMsgBeginRedelegate(delAddr sdk.AccAddress, valSrcAddr,
valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) MsgBeginRedelegate {
valDstAddr sdk.ValAddress, amount sdk.Coin) MsgBeginRedelegate {
return MsgBeginRedelegate{
DelegatorAddress: delAddr,
ValidatorSrcAddress: valSrcAddr,
ValidatorDstAddress: valDstAddr,
SharesAmount: sharesAmount,
Amount: amount,
}
}
@ -295,7 +295,7 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
if msg.ValidatorDstAddress.Empty() {
return ErrNilValidatorAddr(DefaultCodespace)
}
if msg.SharesAmount.LTE(sdk.ZeroDec()) {
if msg.Amount.Amount.LTE(sdk.ZeroInt()) {
return ErrBadSharesAmount(DefaultCodespace)
}
return nil
@ -305,14 +305,14 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
type MsgUndelegate struct {
DelegatorAddress sdk.AccAddress `json:"delegator_address"`
ValidatorAddress sdk.ValAddress `json:"validator_address"`
SharesAmount sdk.Dec `json:"shares_amount"`
Amount sdk.Coin `json:"amount"`
}
func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) MsgUndelegate {
func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgUndelegate {
return MsgUndelegate{
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
SharesAmount: sharesAmount,
Amount: amount,
}
}
@ -335,7 +335,7 @@ func (msg MsgUndelegate) ValidateBasic() sdk.Error {
if msg.ValidatorAddress.Empty() {
return ErrNilValidatorAddr(DefaultCodespace)
}
if msg.SharesAmount.LTE(sdk.ZeroDec()) {
if msg.Amount.Amount.LTE(sdk.ZeroInt()) {
return ErrBadSharesAmount(DefaultCodespace)
}
return nil

View File

@ -110,19 +110,18 @@ func TestMsgBeginRedelegate(t *testing.T) {
delegatorAddr sdk.AccAddress
validatorSrcAddr sdk.ValAddress
validatorDstAddr sdk.ValAddress
sharesAmount sdk.Dec
amount sdk.Coin
expectPass bool
}{
{"regular", sdk.AccAddress(addr1), addr2, addr3, sdk.NewDecWithPrec(1, 1), true},
{"negative decimal", sdk.AccAddress(addr1), addr2, addr3, sdk.NewDecWithPrec(-1, 1), false},
{"zero amount", sdk.AccAddress(addr1), addr2, addr3, sdk.ZeroDec(), false},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, addr3, sdk.NewDecWithPrec(1, 1), false},
{"empty source validator", sdk.AccAddress(addr1), emptyAddr, addr3, sdk.NewDecWithPrec(1, 1), false},
{"empty destination validator", sdk.AccAddress(addr1), addr2, emptyAddr, sdk.NewDecWithPrec(1, 1), false},
{"regular", sdk.AccAddress(addr1), addr2, addr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true},
{"zero amount", sdk.AccAddress(addr1), addr2, addr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, addr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false},
{"empty source validator", sdk.AccAddress(addr1), emptyAddr, addr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false},
{"empty destination validator", sdk.AccAddress(addr1), addr2, emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false},
}
for _, tc := range tests {
msg := NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.sharesAmount)
msg := NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.amount)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
@ -137,18 +136,17 @@ func TestMsgUndelegate(t *testing.T) {
name string
delegatorAddr sdk.AccAddress
validatorAddr sdk.ValAddress
sharesAmount sdk.Dec
amount sdk.Coin
expectPass bool
}{
{"regular", sdk.AccAddress(addr1), addr2, sdk.NewDecWithPrec(1, 1), true},
{"negative decimal", sdk.AccAddress(addr1), addr2, sdk.NewDecWithPrec(-1, 1), false},
{"zero amount", sdk.AccAddress(addr1), addr2, sdk.ZeroDec(), false},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, sdk.NewDecWithPrec(1, 1), false},
{"empty validator", sdk.AccAddress(addr1), emptyAddr, sdk.NewDecWithPrec(1, 1), false},
{"regular", sdk.AccAddress(addr1), addr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true},
{"zero amount", sdk.AccAddress(addr1), addr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false},
{"empty validator", sdk.AccAddress(addr1), emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false},
}
for _, tc := range tests {
msg := NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.sharesAmount)
msg := NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.amount)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {

View File

@ -354,7 +354,12 @@ func (v Validator) AddTokensFromDel(pool Pool, amount sdk.Int) (Validator, Pool,
// the first delegation to a validator sets the exchange rate to one
issuedShares = amount.ToDec()
} else {
issuedShares = v.DelegatorShares.MulInt(amount).QuoInt(v.Tokens)
shares, err := v.SharesFromTokens(amount)
if err != nil {
panic(err)
}
issuedShares = shares
}
if v.Status == sdk.Bonded {
@ -384,7 +389,7 @@ func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Dec) (Validator, Poo
// leave excess tokens in the validator
// however fully use all the delegator shares
issuedTokens = v.ShareTokens(delShares).TruncateInt()
issuedTokens = v.TokensFromShares(delShares).TruncateInt()
v.Tokens = v.Tokens.Sub(issuedTokens)
if v.Tokens.IsNegative() {
panic("attempting to remove more tokens than available in validator")
@ -407,15 +412,35 @@ func (v Validator) InvalidExRate() bool {
}
// calculate the token worth of provided shares
func (v Validator) ShareTokens(shares sdk.Dec) sdk.Dec {
func (v Validator) TokensFromShares(shares sdk.Dec) sdk.Dec {
return (shares.MulInt(v.Tokens)).Quo(v.DelegatorShares)
}
// calculate the token worth of provided shares, truncated
func (v Validator) ShareTokensTruncated(shares sdk.Dec) sdk.Dec {
func (v Validator) TokensFromSharesTruncated(shares sdk.Dec) sdk.Dec {
return (shares.MulInt(v.Tokens)).QuoTruncate(v.DelegatorShares)
}
// SharesFromTokens returns the shares of a delegation given a bond amount. It
// returns an error if the validator has no tokens.
func (v Validator) SharesFromTokens(amt sdk.Int) (sdk.Dec, sdk.Error) {
if v.Tokens.IsZero() {
return sdk.ZeroDec(), ErrInsufficientShares(DefaultCodespace)
}
return v.GetDelegatorShares().MulInt(amt).QuoInt(v.GetTokens()), nil
}
// SharesFromTokensTruncated returns the truncated shares of a delegation given
// a bond amount. It returns an error if the validator has no tokens.
func (v Validator) SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, sdk.Error) {
if v.Tokens.IsZero() {
return sdk.ZeroDec(), ErrInsufficientShares(DefaultCodespace)
}
return v.GetDelegatorShares().MulInt(amt).QuoTruncate(v.GetTokens().ToDec()), nil
}
// get the bonded tokens which the validator holds
func (v Validator) BondedTokens() sdk.Int {
if v.Status == sdk.Bonded {

View File

@ -77,11 +77,11 @@ func TestShareTokens(t *testing.T) {
Tokens: sdk.NewInt(100),
DelegatorShares: sdk.NewDec(100),
}
assert.True(sdk.DecEq(t, sdk.NewDec(50), validator.ShareTokens(sdk.NewDec(50))))
assert.True(sdk.DecEq(t, sdk.NewDec(50), validator.TokensFromShares(sdk.NewDec(50))))
validator.Tokens = sdk.NewInt(50)
assert.True(sdk.DecEq(t, sdk.NewDec(25), validator.ShareTokens(sdk.NewDec(50))))
assert.True(sdk.DecEq(t, sdk.NewDec(5), validator.ShareTokens(sdk.NewDec(10))))
assert.True(sdk.DecEq(t, sdk.NewDec(25), validator.TokensFromShares(sdk.NewDec(50))))
assert.True(sdk.DecEq(t, sdk.NewDec(5), validator.TokensFromShares(sdk.NewDec(10))))
}
func TestRemoveTokens(t *testing.T) {