Skip to content

Commit

Permalink
Merge PR #2405: Unbonding and Redelegations Queue
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnya97 authored and cwgoes committed Oct 8, 2018
1 parent d661ccb commit cd21427
Show file tree
Hide file tree
Showing 23 changed files with 375 additions and 545 deletions.
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ BREAKING CHANGES
* [x/gov] [#2195] Governance uses BFT Time
* [x/gov] \#2256 Removed slashing for governance non-voting validators
* [simulation] \#2162 Added back correct supply invariants
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker

* SDK
* [core] \#2219 Update to Tendermint 0.24.0
Expand Down
2 changes: 0 additions & 2 deletions cmd/gaia/app/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
{100, stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper)},
{100, stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper)},
{100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)},
}
}
Expand Down
31 changes: 31 additions & 0 deletions docs/spec/staking/end_block.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,34 @@ EndBlock() ValidatorSetChanges
ClearTendermintUpdates()
return vsc
```

## CompleteUnbonding

Complete the unbonding and transfer the coins to the delegate. Realize any
slashing that occurred during the unbonding period.

```golang
unbondingQueue(currTime time.Time):
// unbondings are in ordered queue from oldest to newest
for all unbondings whose CompleteTime < currTime:
validator = GetValidator(unbonding.ValidatorAddr)
AddCoins(unbonding.DelegatorAddr, unbonding.Balance)
removeUnbondingDelegation(unbonding)
return
```

## CompleteRedelegation

Note that unlike CompleteUnbonding slashing of redelegating shares does not
take place during completion. Slashing on redelegated shares takes place
actively as a slashing occurs. The redelegation completion queue serves simply to
clean up state, as redelegations older than an unbonding period need not be kept,
as that is the max time that their old validator's evidence can be used to slash them.

```golang
redelegationQueue(currTime time.Time):
// redelegations are in ordered queue from oldest to newest
for all redelegations whose CompleteTime < currTime:
removeRedelegation(redelegation)
return
```
43 changes: 0 additions & 43 deletions docs/spec/staking/transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ corresponding updates to the state. Transactions:
* TxEditValidator
* TxDelegation
* TxStartUnbonding
* TxCompleteUnbonding
* TxRedelegate
* TxCompleteRedelegation

Other important state changes:

Expand Down Expand Up @@ -188,27 +186,6 @@ startUnbonding(tx TxStartUnbonding):
return
```
### TxCompleteUnbonding
Complete the unbonding and transfer the coins to the delegate. Perform any
slashing that occurred during the unbonding period.
```golang
type TxUnbondingComplete struct {
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
}

redelegationComplete(tx TxRedelegate):
unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator)
if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight
validator = GetValidator(tx.ValidatorAddr)
returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio
AddCoins(unbonding.DelegatorAddr, returnTokens)
removeUnbondingDelegation(unbonding)
return
```
### TxRedelegation
The redelegation command allows delegators to instantly switch validators. Once
Expand Down Expand Up @@ -243,26 +220,6 @@ redelegate(tx TxRedelegate):
return
```
### TxCompleteRedelegation
Note that unlike TxCompleteUnbonding slashing of redelegating shares does not
take place during completion. Slashing on redelegated shares takes place
actively as a slashing occurs.
```golang
type TxRedelegationComplete struct {
DelegatorAddr Address
ValidatorFrom Validator
ValidatorTo Validator
}

redelegationComplete(tx TxRedelegate):
redelegation = getRedelegation(tx.DelegatorAddr, tx.validatorFrom, tx.validatorTo)
if redelegation.CompleteTime >= CurrentBlockTime && redelegation.CompleteHeight >= CurrentBlockHeight
removeRedelegation(redelegation)
return
```
### Update Validators
Within many transactions the validator set must be updated based on changes in
Expand Down
7 changes: 7 additions & 0 deletions types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package types
import (
"context"
"sync"
"time"

"github.com/golang/protobuf/proto"

Expand Down Expand Up @@ -181,6 +182,12 @@ func (c Context) WithBlockHeader(header abci.Header) Context {
return c.withValue(contextKeyBlockHeader, header)
}

func (c Context) WithBlockTime(newTime time.Time) Context {
newHeader := c.BlockHeader()
newHeader.Time = newTime
return c.WithBlockHeader(newHeader)
}

func (c Context) WithBlockHeight(height int64) Context {
return c.withValue(contextKeyBlockHeight, height)
}
Expand Down
7 changes: 7 additions & 0 deletions types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,13 @@ func PrefixEndBytes(prefix []byte) []byte {
return end
}

// InclusiveEndBytes returns the []byte that would end a
// range query such that the input would be included
func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) {
exclusiveBytes = append(inclusiveBytes, byte(0x00))
return exclusiveBytes
}

// TransientStoreKey is used for indexing transient stores in a MultiStore
type TransientStoreKey struct {
name string
Expand Down
5 changes: 2 additions & 3 deletions x/slashing/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ func TestJailedValidatorDelegations(t *testing.T) {
got = stake.NewHandler(stakeKeeper)(ctx, msgBeginUnbonding)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got: %v", got)

msgCompleteUnbonding := stake.NewMsgCompleteUnbonding(sdk.AccAddress(valAddr), valAddr)
got = stake.NewHandler(stakeKeeper)(ctx, msgCompleteUnbonding)
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got: %v", got)
err := stakeKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr)
require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err)

// verify validator still exists and is jailed
validator, found := stakeKeeper.GetValidator(ctx, valAddr)
Expand Down
79 changes: 0 additions & 79 deletions x/stake/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
cmd.AddCommand(
client.PostCommands(
GetCmdBeginRedelegate(storeName, cdc),
GetCmdCompleteRedelegate(cdc),
)...)

return cmd
Expand Down Expand Up @@ -270,47 +269,6 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
return cmd
}

// GetCmdCompleteRedelegate implements the complete redelegation command.
func GetCmdCompleteRedelegate(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete redelegation",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))

delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}

valSrcAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidatorSrc))
if err != nil {
return err
}

valDstAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}

msg := stake.NewMsgCompleteRedelegate(delAddr, valSrcAddr, valDstAddr)

if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}

cmd.Flags().AddFlagSet(fsRedelegation)

return cmd
}

// GetCmdUnbond implements the unbond validator command.
func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -321,7 +279,6 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command {
cmd.AddCommand(
client.PostCommands(
GetCmdBeginUnbonding(storeName, cdc),
GetCmdCompleteUnbonding(cdc),
)...)

return cmd
Expand Down Expand Up @@ -374,39 +331,3 @@ func GetCmdBeginUnbonding(storeName string, cdc *codec.Codec) *cobra.Command {

return cmd
}

// GetCmdCompleteUnbonding implements the complete unbonding validator command.
func GetCmdCompleteUnbonding(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete unbonding",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))

delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}

valAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}

msg := stake.NewMsgCompleteUnbonding(delAddr, valAddr)

if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}

cmd.Flags().AddFlagSet(fsValidator)

return cmd
}
84 changes: 5 additions & 79 deletions x/stake/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,18 @@ type (
SharesAmount string `json:"shares"`
}

msgCompleteRedelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
}

msgBeginUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
SharesAmount string `json:"shares"`
}

msgCompleteUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
}

// the request body for edit delegations
EditDelegationsReq struct {
BaseReq utils.BaseReq `json:"base_req"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
BaseReq utils.BaseReq `json:"base_req"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
}
)

Expand Down Expand Up @@ -106,9 +93,7 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
// build messages
messages := make([]sdk.Msg, len(req.Delegations)+
len(req.BeginRedelegates)+
len(req.CompleteRedelegates)+
len(req.BeginUnbondings)+
len(req.CompleteUnbondings))
len(req.BeginUnbondings))

i := 0
for _, msg := range req.Delegations {
Expand Down Expand Up @@ -177,39 +162,6 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
i++
}

for _, msg := range req.CompleteRedelegates {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}

valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}

valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}

if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}

messages[i] = stake.MsgCompleteRedelegate{
DelegatorAddr: delAddr,
ValidatorSrcAddr: valSrcAddr,
ValidatorDstAddr: valDstAddr,
}

i++
}

for _, msg := range req.BeginUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
Expand Down Expand Up @@ -243,32 +195,6 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
i++
}

for _, msg := range req.CompleteUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}

valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}

if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}

messages[i] = stake.MsgCompleteUnbonding{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
}

i++
}

simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
Expand Down
Loading

0 comments on commit cd21427

Please sign in to comment.