feat: add validator vesting spec

This commit is contained in:
Kevin Davis 2019-09-23 15:12:18 -04:00
parent 918a43e7ab
commit 8359a819ad
5 changed files with 85 additions and 5 deletions

View File

@ -41,8 +41,9 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t { if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key, i) k.UpdateVestedCoinsProgress(ctx, key, i)
} }
k.HandleVestingDebt(ctx, key, currentBlockTime)
} }
// handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key, currentBlockTime)
} }
k.SetPreviousBlockTime(ctx, currentBlockTime) k.SetPreviousBlockTime(ctx, currentBlockTime)
} }

View File

@ -14,7 +14,7 @@ import (
) )
// Assert ValidatorVestingAccount implements the vestexported.VestingAccount interface // Assert ValidatorVestingAccount implements the vestexported.VestingAccount interface
// Assert // Assert ValidatorVestingAccount implements the authexported.GenesisAccount interface
var _ vestexported.VestingAccount = (*ValidatorVestingAccount)(nil) var _ vestexported.VestingAccount = (*ValidatorVestingAccount)(nil)
var _ authexported.GenesisAccount = (*ValidatorVestingAccount)(nil) var _ authexported.GenesisAccount = (*ValidatorVestingAccount)(nil)
@ -25,8 +25,10 @@ func init() {
// ValidatorVestingAccount implements the VestingAccount interface. It // ValidatorVestingAccount implements the VestingAccount interface. It
// conditionally vests by unlocking coins during each specified period, provided // conditionally vests by unlocking coins during each specified period, provided
// that the validator address has validated at least **threshold** blocks during // that the validator address has validated at least **SigningThreshold** blocks during
// the previous vesting period. If the validator has not vested at least the threshold, // the previous vesting period. The signing threshold takes values 0 to 100 are represents the
// percentage of blocks that must be signed each period for the vesting to complete successfully.
// If the validator has not signed at least the threshold percentage of blocks during a period,
// the coins are returned to the return address, or burned if the return address is null. // the coins are returned to the return address, or burned if the return address is null.
type ValidatorVestingAccount struct { type ValidatorVestingAccount struct {
*vesting.PeriodicVestingAccount *vesting.PeriodicVestingAccount
@ -35,7 +37,7 @@ type ValidatorVestingAccount struct {
SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"` SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"`
MissingSignCount []int64 `json:"missing_sign_count" yaml:"missing_sign_count"` MissingSignCount []int64 `json:"missing_sign_count" yaml:"missing_sign_count"`
VestingPeriodProgress []int `json:"vesting_period_progress" yaml:"vesting_period_progress"` VestingPeriodProgress []int `json:"vesting_period_progress" yaml:"vesting_period_progress"`
DebtAfterFailedVesting sdk.Coins DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"`
} }
// NewValidatorVestingAccountRaw creates a new ValidatorVestingAccount object from BaseVestingAccount // NewValidatorVestingAccountRaw creates a new ValidatorVestingAccount object from BaseVestingAccount

View File

@ -0,0 +1,8 @@
# Concepts
The validator-vesting module is responsible for managing Validator Vesting Accounts, a vesting account for which the release of coins is tied to the validation of the blockchain. Validator Vesting Accounts implemnt the cosmos-sdk vesting account spec, which can be found [here](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec).
The main concept the Validator Vesting Account introduces is that of _conditional vesting_, or vesting accounts in which it is possible for some or all of the vesting coins to fail to vest. For Validator Vesting Accounts, vesting is broken down into user-specified __vesting periods__. Each vesting period specifies an amount of coins that could vest in that period, and how long the vesting period lasts.
For each vesting period, a __signing threshold__ is specified, which is the percentage of blocks that must be signed for the coins to successfully vest. After a period ends, coins that are successfully vested become freely spendable. Coins that do not successfuly vest are burned, or sent to an optional return address.

View File

@ -0,0 +1,25 @@
# State
## Validator Vesting Account type
Validator Vesting Accounts implement the `cosmos-sdk` vesting account spec and extend the `PeriodicVestingAccountType`:
```go
type ValidatorVestingAccount struct {
*vesting.PeriodicVestingAccount
ValidatorAddress sdk.ConsAddress // The validator address which will be used to check if blocks were signed
ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"` // The account where coins will be returned in the event of a failed vesting period
SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"` // The percentage of blocks, as an integer between 0 and 100, that must be signed each period for coins to successfully vest.
MissingSignCount []int64 `json:"missing_sign_count" yaml:"missing_sign_count"` // An array of two integers which track the number of blocks that were not signed during the current period and the total number of blocks which have passed during the current period, respectively.
VestingPeriodProgress []int `json:"vesting_period_progress" yaml:"vesting_period_progress"` //An array with length equal to the number of vesting periods. After each period, the value at the index of that period is updated with 0 for unsucessful vesting and 1 for successful vesting.
DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` // The debt currently owed by the account. Debt accumulates in the event of unsuccessful vesting periods.
}
```
## Stores
There is one `KVStore` in `validator-vesting` which stores
* A mapping from each ValidatorVestingAccount `address` to `[]Byte{0}`
* A mapping from `previous_block_time_prefix` to `time.Time`
The use of `[]Byte{0}` value for each `address` key reflects that this module only accesses the store to get or iterate over keys, and does not require storing an value.

View File

@ -0,0 +1,44 @@
# Begin Block
At each `BeginBlock`, all validator vesting accounts are iterated over to update the status of the current vesting period. Note that the address of each account is retreived by iterating over the keys in the `validator-vesting` store, while the account objects are stored and accessed using the `auth` module's `AccountKeeper`. For each account, the block count is incremented, the missed sign count is incremented if the validator did not sign the block or was not found in the validator set. By comparing the blocktime of the current `BeginBlock`, with the value of `previousBlockTime` stored in the `validator-vesting` store, it is determined if the end of the current period has been reached. If the current period has ended, the `VestingPeriodProgress` field is updated to reflect if the coins for the ending period successfully vested or not. After updates are made regarding the status of the current vesting period, any outstanding debt on the account is attempted to be collected. If there is enough `SpendableBalance` on the account to cover the debt, coins are sent to the `ReturnAdress` or burned. If there is not enough `SpendableBalance` to cover the debt, all delegations of the account are `Unbonded`. Once those unbonding events reach maturity, the coins freed from the undonding will be used to cover the debt.
```go
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
previousBlockTime := time.Time{}
if ctx.BlockHeight() > 1 {
previousBlockTime = k.GetPreviousBlockTime(ctx)
}
currentBlockTime := req.Header.GetTime()
var voteInfos VoteInfos
voteInfos = ctx.VoteInfos()
validatorVestingKeys := k.GetAllAccountKeys(ctx)
for _, key := range validatorVestingKeys {
acc := k.GetAccountFromAuthKeeper(ctx, key)
if voteInfos.ContainsValidatorAddress(acc.ValidatorAddress) {
vote := voteInfos.MustFilterByValidatorAddress(acc.ValidatorAddress)
if !vote.SignedLastBlock {
// if the validator explicitly missed signing the block, increment the missing sign count
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
} else {
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
}
} else {
// if the validator was not a voting member of the validator set, increment the missing sign count
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
}
// check if a period ended in the last block
endTimes := k.GetPeriodEndTimes(ctx, key)
for i, t := range endTimes {
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key, i)
}
}
// handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key, currentBlockTime)
}
k.SetPreviousBlockTime(ctx, currentBlockTime)
}
```