Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Unbonding and Redelegations Queue #2405

Merged
merged 9 commits into from
Oct 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
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:
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
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