From bff0b361957f8a02fca4b89736afcbbd3315d431 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 11:22:34 -0400 Subject: [PATCH 01/20] Update vesting spec (initial) --- docs/spec/auth/vesting.md | 46 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index c5c25ecaed03..989ce6226666 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -1,39 +1,47 @@ -## Vesting +# Vesting -### Intro and Requirements +## Intro and Requirements -This paper specifies vesting account implementation for the Cosmos Hub. -The requirements for this vesting account is that it should be initialized during genesis with -a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators -and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. -The vesting account should also be able to spend any coins it receives from other users. -Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their +This paper specifies vesting account implementation for the Cosmos Hub. +The requirements for this vesting account is that it should be initialized +during genesis with a starting balance `X` coins and a vesting end time `T`. + +The owner of this account should be able to delegate to validators +and vote with locked coins, however they cannot send locked coins to other +accounts until those coins have been unlocked. + +In addition, the vesting account should also be able to spend any coins it +receives from other users. Thus, the bank module's `MsgSend` handler should +error if a vesting account is trying to send an amount that exceeds their unlocked coin amount. -### Implementation +## Implementation -##### Vesting Account implementation +### Vesting Account implementation -NOTE: `Now = ctx.BlockHeader().Time` +Given, `Now := ctx.BlockHeader().Time` ```go type VestingAccount interface { Account - AssertIsVestingAccount() // existence implies that account is vesting. + AssertIsVestingAccount() // existence implies that account is vesting - // Calculates amount of coins that can be sent to other accounts given the current time + // Calculates the amount of coins that can be sent to other accounts given + // the current time. SendableCoins(sdk.Context) sdk.Coins } -// Implements Vesting Account -// Continuously vests by unlocking coins linearly with respect to time +// ContinuousVestingAccount implements the Vesting Account interface. It +// continuously vests by unlocking coins linearly with respect to time. type ContinuousVestingAccount struct { BaseAccount - OriginalVestingCoins sdk.Coins // Coins in account on Initialization - ReceivedCoins sdk.Coins // Coins received from other accounts - SentCoins sdk.Coins // Coins sent to other accounts - // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point + OriginalVesting sdk.Coins // coins in account upon initialization + DelegatedVesting sdk.Coins // coins that vesting and delegated + DelegatedFree sdk.Coins // coins that are vested and delegated + + // StartTime and EndTime are used to calculate how much of OriginalVesting + // is unlocked at any given point. StartTime time.Time EndTime time.Time } From 77bcc71597ee4bc00350a29f0f1bb640b5504c96 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 16:00:27 -0400 Subject: [PATCH 02/20] Complete updated vesting spec --- docs/spec/auth/vesting.md | 163 ++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 96 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 989ce6226666..05a4345a76bd 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -15,11 +15,7 @@ receives from other users. Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their unlocked coin amount. -## Implementation - -### Vesting Account implementation - -Given, `Now := ctx.BlockHeader().Time` +## Vesting Account Definition ```go type VestingAccount interface { @@ -45,123 +41,98 @@ type ContinuousVestingAccount struct { StartTime time.Time EndTime time.Time } +``` -// Uses time in context to calculate total unlocked coins -SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: - - // Coins unlocked by vesting schedule - unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime) - - // Must still check for currentCoins constraint since some unlocked coins may have been delegated. - currentCoins := vacc.BaseAccount.GetCoins() +## Vesting Account Implementation - // min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins - return min(unlockedCoins, currentCoins) +Given a vesting account, we may further define the following: +```go +OV = OriginalVesting (constant) +V = Percentage of OV that is still vesting (derived by OV and the start/end times) +DV = DelegatedVesting (variable) +DF = DelegatedFree (variable) +BC (BaseAccount.Coins) = OV - transferred (can be negative) - delegated (DV + DF) ``` -The `VestingAccount` interface is used to assert that an account is a vesting account like so: +### Operations -```go -vacc, ok := acc.(VestingAccount); ok -``` +#### Determining Vesting Amount -as well as to calculate the SendableCoins at any given moment. +To determine the amount of coins that are vested for a given block `B`, the following is performed: -The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`, -`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. -Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements -the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of -both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period. +1. Compute `X := B.Time - StartTime` +2. Compute `Y := EndTime - StartTime` +3. Compute `V' := OV * (X / Y)` -##### Changes to Keepers/Handler +Thus, `V := OV - V'` -Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be -handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like -`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. +#### Transferring/Sending -```go -if acc is VestingAccount and Now < vestingAccount.EndTime: - // Check if amount is less than currently allowed sendable coins - if msg.Amount > vestingAccount.SendableCoins(ctx) then fail - else: - vestingAccount.SentCoins += msg.Amount +At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`. -else: - // Account has fully vested, treat like regular account - if msg.Amount > account.GetCoins() then fail +#### Delegating -// All checks passed, send the coins -SendCoins(inputs, outputs) +For a vesting account attempting to delegate `D` coins, the following is performed: -``` +1. Verify `BC >= D > 0` +2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting) +3. Compute `Y := D - X` (portion of `D` that is free) +4. Set `DV += X` +5. Set `DF += Y` +6. Set `BC -= (X + Y)` + +#### Undelegating + +For a vesting account attempting to undelegate `D` coins, the following is performed: + +1. Verify `(DV + DF) >= D > 0` (this is simply a sanity check) +2. Compute `Y := min(DF, D)` (portion of `D` that should become free, prioritizing free coins) +3. Compute `X := D - Y` (portion of `D` that should remain vesting) +4. Set `DV -= X` +5. Set `DF -= Y` +6. Set `BC += D` -Coins that are sent to a vesting account after initialization by users sending them coins should be spendable -immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not -originally own should increment `ReceivedCoins` by the amount sent. -Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute. +## Keepers & Handlers -CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`. -However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. +Since a vesting account should be capable of doing everything but sending with +its locked coins, the restriction should be handled at the `bank.Keeper` level. +Specifically, in methods that are explicitly used for sending like `sendCoins` and +`inputOutputCoins`. These methods must check that an account is a vesting account. -### Initializing at Genesis +## Initializing at Genesis -To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be -BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts -as appropriate. +To initialize both vesting accounts and base accounts, the `GenesisAccount` +struct will include an `EndTime`. Accounts meant to be of type `BaseAccount` will +have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into +BaseAccounts and VestingAccounts as appropriate. ```go type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - GenesisCoins sdk.Coins `json:"coins"` - EndTime int64 `json:"lock"` + Address sdk.AccAddress + GenesisCoins sdk.Coins + EndTime int64 } -initChainer: - for gacc in GenesisAccounts: +func initChainer() { + for genAcc in GenesisAccounts { baseAccount := BaseAccount{ - Address: gacc.Address, - Coins: gacc.GenesisCoins, + Address: genAcc.Address, + Coins: genAcc.GenesisCoins, } - if gacc.EndTime != 0: - vestingAccount := ContinuouslyVestingAccount{ - BaseAccount: baseAccount, - OriginalVestingCoins: gacc.GenesisCoins, - StartTime: RequestInitChain.Time, - EndTime: gacc.EndTime, + + if genAcc.EndTime != 0 { + vestingAccount := ContinuousVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: genAcc.GenesisCoins, + StartTime: RequestInitChain.Time, + EndTime: genAcc.EndTime, } + AddAccountToState(vestingAccount) - else: + } else { AddAccountToState(baseAccount) - + } + } +} ``` - -### Formulas - -`OriginalVestingCoins`: Amount of coins in account at Genesis - -`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) - -`ReceivedCoins`: Coins received from other accounts (always unlocked) - -`LockedCoins`: Coins that are currently locked - -`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked) - -`Sent`: Coins sent to other accounts (MUST be unlocked) - -Maximum amount of coins vesting schedule allows to be sent: - -`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)` - -`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins` - -Coins currently in Account: - -`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent` - -`CurrentCoins = vestingAccount.GetCoins()` - -**Maximum amount of coins spendable right now:** - -`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )` From a03d2f496ca8e45028c3c4b2260e7d062a414570 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 16:02:32 -0400 Subject: [PATCH 03/20] Update code snippet lang for defs --- docs/spec/auth/vesting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 05a4345a76bd..c8120af3e823 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -47,7 +47,7 @@ type ContinuousVestingAccount struct { Given a vesting account, we may further define the following: -```go +``` OV = OriginalVesting (constant) V = Percentage of OV that is still vesting (derived by OV and the start/end times) DV = DelegatedVesting (variable) From 6431c9427eca5d049dbac0642bc9894a1c5b655d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Oct 2018 09:59:11 -0400 Subject: [PATCH 04/20] Address PR review in intro --- docs/spec/auth/vesting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index c8120af3e823..9bf612ba86ed 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -10,11 +10,17 @@ The owner of this account should be able to delegate to validators and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. +When it comes to governance, it is yet undefined if we want to allow a vesting +account to be able to deposit vesting coins into proposals. + In addition, the vesting account should also be able to spend any coins it receives from other users. Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their unlocked coin amount. +__Note__: A vesting account could have some vesting and non-vesting coins at +genesis, however, the latter is unlikely. + ## Vesting Account Definition ```go From 8bbc2d69f8c96815585c10d0b57aa6eb900a6a2e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Oct 2018 10:09:29 -0400 Subject: [PATCH 05/20] Add delayed (discrete) vesting account struct --- docs/spec/auth/vesting.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 9bf612ba86ed..d68681ba3124 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -33,20 +33,31 @@ type VestingAccount interface { SendableCoins(sdk.Context) sdk.Coins } -// ContinuousVestingAccount implements the Vesting Account interface. It +// ContinuousVestingAccount implements the VestingAccount interface. It // continuously vests by unlocking coins linearly with respect to time. type ContinuousVestingAccount struct { BaseAccount - OriginalVesting sdk.Coins // coins in account upon initialization - DelegatedVesting sdk.Coins // coins that vesting and delegated - DelegatedFree sdk.Coins // coins that are vested and delegated + OriginalVesting sdk.Coins // coins in account upon initialization + DelegatedVesting sdk.Coins // coins that vesting and delegated + DelegatedFree sdk.Coins // coins that are vested and delegated // StartTime and EndTime are used to calculate how much of OriginalVesting // is unlocked at any given point. StartTime time.Time EndTime time.Time } + +// DelayedVestingAccount implements the VestingAccount interface. It vests all +// coins after a specific time, but non prior. In other words, it keeps them +// locked until a specified time. +type DelayedVestingAccount struct { + BaseAccount + + OriginalVesting sdk.Coins // coins in account upon initialization + + EndTime time.Time // when the coins become unlocked +} ``` ## Vesting Account Implementation From 6d753ba8eaee914b05fde8be2759a3676a9125b0 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Oct 2018 10:31:12 -0400 Subject: [PATCH 06/20] Refactor handlers/keepers intro --- docs/spec/auth/vesting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index d68681ba3124..99f8288c9c96 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -112,10 +112,10 @@ For a vesting account attempting to undelegate `D` coins, the following is perfo ## Keepers & Handlers -Since a vesting account should be capable of doing everything but sending with -its locked coins, the restriction should be handled at the `bank.Keeper` level. -Specifically, in methods that are explicitly used for sending like `sendCoins` and -`inputOutputCoins`. These methods must check that an account is a vesting account. +The `VestingAccount` implementations reside in `x/auth`. However, any keeper in +a module (e.g. staking in `x/stake`) wishing to potentially utilize any vesting +coins, must call explicit methods on the `BankKeeper` (e.g. `DelegateCoins`) +opposed to `SendCoins` and `SubtractCoins`. ## Initializing at Genesis From e9295252251417c86f29b294563228f076652d5b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Oct 2018 11:25:59 -0400 Subject: [PATCH 07/20] More spec updates and reformatting --- docs/spec/auth/vesting.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 99f8288c9c96..f6529930300e 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -13,10 +13,8 @@ accounts until those coins have been unlocked. When it comes to governance, it is yet undefined if we want to allow a vesting account to be able to deposit vesting coins into proposals. -In addition, the vesting account should also be able to spend any coins it -receives from other users. Thus, the bank module's `MsgSend` handler should -error if a vesting account is trying to send an amount that exceeds their -unlocked coin amount. +It is also important to note that a vesting account vests all of it's coin +denominations at the same rate. This may be subject to change. __Note__: A vesting account could have some vesting and non-vesting coins at genesis, however, the latter is unlikely. @@ -62,27 +60,32 @@ type DelayedVestingAccount struct { ## Vesting Account Implementation -Given a vesting account, we may further define the following: +Given a vesting account, we define the following in the proceeding operations: ``` OV = OriginalVesting (constant) -V = Percentage of OV that is still vesting (derived by OV and the start/end times) +V = Number of OV coins that are still vesting (derived by OV and the start/end times) DV = DelegatedVesting (variable) DF = DelegatedFree (variable) BC (BaseAccount.Coins) = OV - transferred (can be negative) - delegated (DV + DF) ``` +__Note__: The above are explicitly stored and modified on the vesting account. + ### Operations #### Determining Vesting Amount -To determine the amount of coins that are vested for a given block `B`, the following is performed: +To determine the amount of coins that are vested for a given block `B`, the +following is performed: 1. Compute `X := B.Time - StartTime` 2. Compute `Y := EndTime - StartTime` 3. Compute `V' := OV * (X / Y)` -Thus, `V := OV - V'` +Thus, the number of vested coins is defined by `V'` and as a result the number of +vesting coins equates to `V := OV - V'`. It is important to note these values are +calculated on demand and not on a per-block basis. #### Transferring/Sending @@ -97,7 +100,7 @@ For a vesting account attempting to delegate `D` coins, the following is perform 3. Compute `Y := D - X` (portion of `D` that is free) 4. Set `DV += X` 5. Set `DF += Y` -6. Set `BC -= (X + Y)` +6. Set `BC -= D` #### Undelegating @@ -117,6 +120,11 @@ a module (e.g. staking in `x/stake`) wishing to potentially utilize any vesting coins, must call explicit methods on the `BankKeeper` (e.g. `DelegateCoins`) opposed to `SendCoins` and `SubtractCoins`. +In addition, the vesting account should also be able to spend any coins it +receives from other users. Thus, the bank module's `MsgSend` handler should +error if a vesting account is trying to send an amount that exceeds their +unlocked coin amount. + ## Initializing at Genesis To initialize both vesting accounts and base accounts, the `GenesisAccount` From a46fd3aca3f0bea0a5fd01cef409def45757b0d2 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Oct 2018 14:48:10 -0400 Subject: [PATCH 08/20] Clear up vesting operation definitions --- docs/spec/auth/vesting.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index f6529930300e..eb764307a9ee 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -19,9 +19,11 @@ denominations at the same rate. This may be subject to change. __Note__: A vesting account could have some vesting and non-vesting coins at genesis, however, the latter is unlikely. -## Vesting Account Definition +## Vesting Account Types ```go +// VestingAccount defines an interface that any vesting account type must +// implement. type VestingAccount interface { Account AssertIsVestingAccount() // existence implies that account is vesting @@ -62,15 +64,12 @@ type DelayedVestingAccount struct { Given a vesting account, we define the following in the proceeding operations: -``` -OV = OriginalVesting (constant) -V = Number of OV coins that are still vesting (derived by OV and the start/end times) -DV = DelegatedVesting (variable) -DF = DelegatedFree (variable) -BC (BaseAccount.Coins) = OV - transferred (can be negative) - delegated (DV + DF) -``` - -__Note__: The above are explicitly stored and modified on the vesting account. +- `OV`: The original vesting coin amount. It is a constant value. +- `V`: The number of `OV` coins that are still **vesting**. It is derived by `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis. +- `V'`: The number of `OV` coins that are **vested** (unlocked). This value is computed on demand and not a per-block basis. +- `DV`: The number of delegated **vesting** coins. It is a variable value. It is stored and modified directly in the vesting account. +- `DF`: The number of delegated **vested** coins. It is a variable value. It is stored and modified directly in the vesting account. +- `BC`: The number of `OV` coins less any coins that are transferred, which can be negative, or delegated (`DV + DF`). It is considered to be balance of the embedded base account. It is stored and modified directly in the vesting account. ### Operations @@ -91,6 +90,10 @@ calculated on demand and not on a per-block basis. At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`. +In other words, a vesting account may transfer the minimum of the base account +balance and the base account balance plus the number of currently delegated +vesting coins less the number of coins vested so far. + #### Delegating For a vesting account attempting to delegate `D` coins, the following is performed: From d9828a7aa89f6d1d5186a854a7ea5e6698db9c1e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 09:26:39 -0400 Subject: [PATCH 09/20] Update spec with pseudo-code --- docs/spec/auth/vesting.md | 260 ++++++++++++++++++++++++++++++++------ 1 file changed, 222 insertions(+), 38 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index eb764307a9ee..072d860e106b 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -8,16 +8,17 @@ during genesis with a starting balance `X` coins and a vesting end time `T`. The owner of this account should be able to delegate to validators and vote with locked coins, however they cannot send locked coins to other -accounts until those coins have been unlocked. +accounts until those coins have been unlocked. When it comes to governance, it +is yet undefined if we want to allow a vesting account to be able to deposit +vesting coins into proposals. -When it comes to governance, it is yet undefined if we want to allow a vesting -account to be able to deposit vesting coins into proposals. +In addition, a vesting account vests all of it's coin denominations at the same +rate. This may be subject to change. -It is also important to note that a vesting account vests all of it's coin -denominations at the same rate. This may be subject to change. - -__Note__: A vesting account could have some vesting and non-vesting coins at -genesis, however, the latter is unlikely. +**Note**: A vesting account could have some vesting and non-vesting coins at +genesis, however, the latter is unsupported at the moment. If such a feature is +required, the `GenesisAccount` type will need to be updated in order to make +such a distinction. ## Vesting Account Types @@ -30,22 +31,31 @@ type VestingAccount interface { // Calculates the amount of coins that can be sent to other accounts given // the current time. - SendableCoins(sdk.Context) sdk.Coins + SpendableCoins(Context) Coins + // Performs delegation accounting. + TrackDelegation(amount) + // Performs undelegation accounting. + TrackUndelegation(amount) +} + +// BaseVestingAccount implements the VestingAccount interface. It contains all +// the necessary fields needed for any vesting account implementation. +type BaseVestingAccount struct { + BaseAccount + + OriginalVesting Coins // coins in account upon initialization + DelegatedFree Coins // coins that are vested and delegated + EndTime Time // when the coins become unlocked } // ContinuousVestingAccount implements the VestingAccount interface. It // continuously vests by unlocking coins linearly with respect to time. type ContinuousVestingAccount struct { BaseAccount + BaseVestingAccount - OriginalVesting sdk.Coins // coins in account upon initialization - DelegatedVesting sdk.Coins // coins that vesting and delegated - DelegatedFree sdk.Coins // coins that are vested and delegated - - // StartTime and EndTime are used to calculate how much of OriginalVesting - // is unlocked at any given point. - StartTime time.Time - EndTime time.Time + DelegatedVesting Coins // coins that vesting and delegated + StartTime Time // when the coins start to vest } // DelayedVestingAccount implements the VestingAccount interface. It vests all @@ -53,27 +63,27 @@ type ContinuousVestingAccount struct { // locked until a specified time. type DelayedVestingAccount struct { BaseAccount - - OriginalVesting sdk.Coins // coins in account upon initialization - - EndTime time.Time // when the coins become unlocked + BaseVestingAccount } ``` -## Vesting Account Implementation +## Vesting Account Specification Given a vesting account, we define the following in the proceeding operations: - `OV`: The original vesting coin amount. It is a constant value. -- `V`: The number of `OV` coins that are still **vesting**. It is derived by `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis. -- `V'`: The number of `OV` coins that are **vested** (unlocked). This value is computed on demand and not a per-block basis. -- `DV`: The number of delegated **vesting** coins. It is a variable value. It is stored and modified directly in the vesting account. -- `DF`: The number of delegated **vested** coins. It is a variable value. It is stored and modified directly in the vesting account. +- `V`: The number of `OV` coins that are still _vesting_. It is derived by `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis. +- `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is computed on demand and not a per-block basis. +- `DV`: The number of delegated _vesting_ coins. It is a variable value. It is stored and modified directly in the vesting account. +- `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable value. It is stored and modified directly in the vesting account. - `BC`: The number of `OV` coins less any coins that are transferred, which can be negative, or delegated (`DV + DF`). It is considered to be balance of the embedded base account. It is stored and modified directly in the vesting account. -### Operations +### Determining Vesting & Vested Amounts + +It is important to note that these values are computed on demand and not on a +mandatory per-block basis. -#### Determining Vesting Amount +#### Continuously Vesting Accounts To determine the amount of coins that are vested for a given block `B`, the following is performed: @@ -81,22 +91,99 @@ following is performed: 1. Compute `X := B.Time - StartTime` 2. Compute `Y := EndTime - StartTime` 3. Compute `V' := OV * (X / Y)` +4. Compute `V := OV - V'` + +Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`, +is _vesting_. + +```go +func (cva ContinuousVestingAccount) GetVestedCoins(b Block) Coins { + x := b.Time - va.StartTime + y := cva.EndTime - cva.StartTime + + return cva.OriginalVesting * (x / y) +} + +func (cva ContinuousVestingAccount) GetVestingCoins(b Block) Coins { + return cva.OriginalVesting - cva.GetVestedCoins(b) +} +``` + +#### Delayed/Discrete Vesting Accounts + +Delayed vesting accounts are easier to reason about as they only have the full +amount vesting up until a certain time, then they all become vested (unlocked). + +```go +func (dva DelayedVestingAccount) GetVestedCoins(b Block) Coins { + if b.Time >= dva.EndTime { + return dva.OriginalVesting + } + + return ZeroCoins +} + +func (dva DelayedVestingAccount) GetVestingCoins(b Block) Coins { + return cva.OriginalVesting - cva.GetVestedCoins(b) +} +``` -Thus, the number of vested coins is defined by `V'` and as a result the number of -vesting coins equates to `V := OV - V'`. It is important to note these values are -calculated on demand and not on a per-block basis. +### Transferring/Sending -#### Transferring/Sending +#### Continuously Vesting Accounts -At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`. +At any given time, a continuous vesting account may transfer: `min((BC + DV) - V, BC)`. In other words, a vesting account may transfer the minimum of the base account balance and the base account balance plus the number of currently delegated vesting coins less the number of coins vested so far. -#### Delegating +```go +func (cva ContinuousVestingAccount) SpendableCoins() Coins { + bc := cva.GetCoins() + return min((bc + cva.DelegatedVesting) - cva.GetVestingCoins(), bc) +} +``` + +##### Delayed/Discrete Vesting Accounts + +A delayed vesting account may send any coins it has received. In addition, if it +has fully vested, it can send any of it's vested coins. + +```go +func (dva DelayedVestingAccount) SpendableCoins() Coins { + bc := cva.GetCoins() + return bc - dva.GetVestingCoins() +} +``` + +##### Keepers/Handlers + +The corresponding `x/bank` keeper should appropriately handle sending coins +based on if the account is a vesting account or not. + +```go +func SendCoins(from Account, to Account amount Coins) { + if isVesting(from) { + sc := from.SpendableCoins() + } else { + sc := from.GetCoins() + } + + if amount <= sc { + from.SetCoins(sc - amount) + to.SetCoins(amount) + // save accounts... + } +} +``` + +### Delegating -For a vesting account attempting to delegate `D` coins, the following is performed: +#### Continuously Vesting Accounts + +For a continuous vesting account attempting to delegate `D` coins, the following +is performed: 1. Verify `BC >= D > 0` 2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting) @@ -105,9 +192,58 @@ For a vesting account attempting to delegate `D` coins, the following is perform 5. Set `DF += Y` 6. Set `BC -= D` -#### Undelegating +```go +func (cva ContinuousVestingAccount) TrackDelegation(amount Coins) { + x := min(max(cva.GetVestingCoins() - cva.DelegatedVesting, 0), amount) + y := amount - x + + cva.DelegatedVesting += x + cva.DelegatedFree += y +} +``` + +##### Delayed/Discrete Vesting Accounts + +For a delayed vesting account, it can only delegate with received coins and +coins that are fully vested so we only need to update `DF`. + +```go +func (dva DelayedVestingAccount) TrackDelegation(amount Coins) { + dva.DelegatedFree += amount +} +``` + +##### Keepers/Handlers -For a vesting account attempting to undelegate `D` coins, the following is performed: +```go +func DelegateCoins(from Account, amount Coins) { + // canDelegate checks different semantics for continuous and delayed vesting + // accounts + if isVesting(from) && canDelegate(from) { + sc := from.GetCoins() + + if amount <= sc { + from.TrackDelegation(amount) + from.SetCoins(sc - amount) + // save account... + } + } else { + sc := from.GetCoins() + + if amount <= sc { + from.SetCoins(sc - amount) + // save account... + } + } +} +``` + +### Undelegating + +#### Continuously Vesting Accounts + +For a continuous vesting account attempting to undelegate `D` coins, the +following is performed: 1. Verify `(DV + DF) >= D > 0` (this is simply a sanity check) 2. Compute `Y := min(DF, D)` (portion of `D` that should become free, prioritizing free coins) @@ -116,11 +252,49 @@ For a vesting account attempting to undelegate `D` coins, the following is perfo 5. Set `DF -= Y` 6. Set `BC += D` +```go +func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) { + y := min(cva.DelegatedFree, amount) + x := amount - y + + cva.DelegatedVesting -= x + cva.DelegatedFree -= y +} +``` + +##### Delayed/Discrete Vesting Accounts + +For a delayed vesting account, it only needs to add back the `DF` amount since +the account is fully vested. + +```go +func (dva DelayedVestingAccount) TrackUndelegation(amount Coins) { + dva.DelegatedFree -= amount +} +``` + +##### Keepers/Handlers + +```go +func UndelegateCoins(to Account, amount Coins) { + if isVesting(to) { + if to.DelegatedFree + to.DelegatedVesting >= amount { + to.TrackUndelegation(amount) + AddCoins(to, amount) + // save account ... + } + } else { + AddCoins(to, amount) + // save account... + } +} +``` + ## Keepers & Handlers The `VestingAccount` implementations reside in `x/auth`. However, any keeper in a module (e.g. staking in `x/stake`) wishing to potentially utilize any vesting -coins, must call explicit methods on the `BankKeeper` (e.g. `DelegateCoins`) +coins, must call explicit methods on the `x/bank` keeper (e.g. `DelegateCoins`) opposed to `SendCoins` and `SubtractCoins`. In addition, the vesting account should also be able to spend any coins it @@ -128,6 +302,8 @@ receives from other users. Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their unlocked coin amount. +See the above specification for full implementation details. + ## Initializing at Genesis To initialize both vesting accounts and base accounts, the `GenesisAccount` @@ -164,3 +340,11 @@ func initChainer() { } } ``` + +## Examples + +TODO: + +## Glossary + +TODO: \ No newline at end of file From fb41713114c6b5404d3e1f868c1c628b3941e62a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 09:29:17 -0400 Subject: [PATCH 10/20] Add TOC --- docs/spec/auth/vesting.md | 52 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 072d860e106b..dcd8c3010afd 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -1,5 +1,29 @@ # Vesting +- [Vesting](#vesting) + - [Intro and Requirements](#intro-and-requirements) + - [Vesting Account Types](#vesting-account-types) + - [Vesting Account Specification](#vesting-account-specification) + - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) + - [Continuously Vesting Accounts](#continuously-vesting-accounts) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) + - [Transferring/Sending](#transferringsending) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-1) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-1) + - [Keepers/Handlers](#keepershandlers) + - [Delegating](#delegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-2) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-2) + - [Keepers/Handlers](#keepershandlers-1) + - [Undelegating](#undelegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-3) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-3) + - [Keepers/Handlers](#keepershandlers-2) + - [Keepers & Handlers](#keepers--handlers) + - [Initializing at Genesis](#initializing-at-genesis) + - [Examples](#examples) + - [Glossary](#glossary) + ## Intro and Requirements This paper specifies vesting account implementation for the Cosmos Hub. @@ -16,7 +40,33 @@ In addition, a vesting account vests all of it's coin denominations at the same rate. This may be subject to change. **Note**: A vesting account could have some vesting and non-vesting coins at -genesis, however, the latter is unsupported at the moment. If such a feature is + + +- [Vesting](#vesting) + - [Intro and Requirements](#intro-and-requirements) + - [Vesting Account Types](#vesting-account-types) + - [Vesting Account Specification](#vesting-account-specification) + - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) + - [Continuously Vesting Accounts](#continuously-vesting-accounts) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) + - [Transferring/Sending](#transferringsending) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-1) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-1) + - [Keepers/Handlers](#keepershandlers) + - [Delegating](#delegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-2) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-2) + - [Keepers/Handlers](#keepershandlers-1) + - [Undelegating](#undelegating) + - [Continuously Vesting Accounts](#continuously-vesting-accounts-3) + - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-3) + - [Keepers/Handlers](#keepershandlers-2) + - [Keepers & Handlers](#keepers--handlers) + - [Initializing at Genesis](#initializing-at-genesis) + - [Examples](#examples) + - [Glossary](#glossary) + + required, the `GenesisAccount` type will need to be updated in order to make such a distinction. From 772f947f9d60f6d192a17c8c2be9f4c8441cd773 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 09:29:52 -0400 Subject: [PATCH 11/20] Remove dup toc --- docs/spec/auth/vesting.md | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index dcd8c3010afd..86f1e3bd6de1 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -1,5 +1,7 @@ # Vesting + + - [Vesting](#vesting) - [Intro and Requirements](#intro-and-requirements) - [Vesting Account Types](#vesting-account-types) @@ -24,6 +26,8 @@ - [Examples](#examples) - [Glossary](#glossary) + + ## Intro and Requirements This paper specifies vesting account implementation for the Cosmos Hub. @@ -40,33 +44,7 @@ In addition, a vesting account vests all of it's coin denominations at the same rate. This may be subject to change. **Note**: A vesting account could have some vesting and non-vesting coins at - - -- [Vesting](#vesting) - - [Intro and Requirements](#intro-and-requirements) - - [Vesting Account Types](#vesting-account-types) - - [Vesting Account Specification](#vesting-account-specification) - - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) - - [Continuously Vesting Accounts](#continuously-vesting-accounts) - - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) - - [Transferring/Sending](#transferringsending) - - [Continuously Vesting Accounts](#continuously-vesting-accounts-1) - - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-1) - - [Keepers/Handlers](#keepershandlers) - - [Delegating](#delegating) - - [Continuously Vesting Accounts](#continuously-vesting-accounts-2) - - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-2) - - [Keepers/Handlers](#keepershandlers-1) - - [Undelegating](#undelegating) - - [Continuously Vesting Accounts](#continuously-vesting-accounts-3) - - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts-3) - - [Keepers/Handlers](#keepershandlers-2) - - [Keepers & Handlers](#keepers--handlers) - - [Initializing at Genesis](#initializing-at-genesis) - - [Examples](#examples) - - [Glossary](#glossary) - required, the `GenesisAccount` type will need to be updated in order to make such a distinction. From 716612f32175f91a846afdfe3bd67713503bd921 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 09:35:32 -0400 Subject: [PATCH 12/20] Add note on slashed delegations --- docs/spec/auth/vesting.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 86f1e3bd6de1..fda10f09749d 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -290,6 +290,10 @@ func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) { } ``` +**Note**: If a delegation is slashed, the continuous vesting account will end up +with excess an `DV` amount, even after all its coins have vested. This is because +undelegating free coins are prioritized. + ##### Delayed/Discrete Vesting Accounts For a delayed vesting account, it only needs to add back the `DF` amount since From e37ef348cfa61f559d7850b459e399da0a7d8986 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 10:22:51 -0400 Subject: [PATCH 13/20] Add simple example --- docs/spec/auth/vesting.md | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index fda10f09749d..ce832d683cb3 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -24,6 +24,8 @@ - [Keepers & Handlers](#keepers--handlers) - [Initializing at Genesis](#initializing-at-genesis) - [Examples](#examples) + - [Simple](#simple) + - [Slashing](#slashing) - [Glossary](#glossary) @@ -375,8 +377,49 @@ func initChainer() { ## Examples -TODO: +### Simple + +Given a continuous vesting account with 10 vesting coins. + +``` +OV := 10 +DF := 0 +DV := 0 +BC := 10 +V := 10 +V' := 0 +``` + +1. Immediately receives 1 coin + ``` + BC := 11 + ``` +2. Time passes, 2 coins vest + ``` + V := 8 + V' := 2 + ``` +3. Delegates 4 coins + ``` + DV := 4 + BC := 7 + ``` +4. Sends 3 coins + ``` + BC := 4 + ``` +5. More time passes, 2 more coins vest + ``` + V := 6 + V' := 4 + ``` +6. Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate + ``` + BC := 2 + ``` + +### Slashing ## Glossary -TODO: \ No newline at end of file +TODO: From e631364012d03ed3b18f6590164e112d948e493c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 10:52:42 -0400 Subject: [PATCH 14/20] Update examples --- docs/spec/auth/vesting.md | 64 ++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index ce832d683cb3..2a3f620d8bdd 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -382,44 +382,74 @@ func initChainer() { Given a continuous vesting account with 10 vesting coins. ``` -OV := 10 -DF := 0 -DV := 0 -BC := 10 -V := 10 -V' := 0 +OV = 10 +DF = 0 +DV = 0 +BC = 10 +V = 10 +V' = 0 ``` 1. Immediately receives 1 coin ``` - BC := 11 + BC = 11 ``` 2. Time passes, 2 coins vest ``` - V := 8 - V' := 2 + V = 8 + V' = 2 ``` -3. Delegates 4 coins +3. Delegates 4 coins to validator A ``` - DV := 4 - BC := 7 + DV = 4 + BC = 7 ``` 4. Sends 3 coins ``` - BC := 4 + BC = 4 ``` 5. More time passes, 2 more coins vest ``` - V := 6 - V' := 4 + V = 6 + V' = 4 ``` -6. Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate +6. Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate. ``` - BC := 2 + BC = 2 ``` ### Slashing +Same initial starting conditions as the simple example. + +1. Time passes, 5 coins vest + ``` + V = 5 + V' = 5 + ``` +2. Delegate 5 coins to validator A + ``` + DV = 5 + BC = 5 + ``` +3. Delegate 5 coins to validator B + ``` + DF = 5 + BC = 0 + ``` +4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins +5. Undelegate from validator A (2.5 coins) + ``` + DF = 5 - 2.5 = 2.5 + BC = 0 + 2.5 = 2.5 + ``` +6. Undelegate from validator B (5 coins) + ``` + DV = 5 - 2.5 = 2.5 + DF = 2.5 - 2.5 = 0 + BC = 2.5 + 5 = 7.5 + ``` + ## Glossary TODO: From 6e83f4c0f7498884b76a96e845bff1db1734a8c7 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 10:55:55 -0400 Subject: [PATCH 15/20] Update examples further --- docs/spec/auth/vesting.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 2a3f620d8bdd..cc9990e01a25 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -443,13 +443,15 @@ Same initial starting conditions as the simple example. DF = 5 - 2.5 = 2.5 BC = 0 + 2.5 = 2.5 ``` -6. Undelegate from validator B (5 coins) +6. Undelegate from validator B (5 coins). The account at this point can only send 2.5 coins unless it receives more coins or until more coins vest. It can still however, delegate. ``` DV = 5 - 2.5 = 2.5 DF = 2.5 - 2.5 = 0 BC = 2.5 + 5 = 7.5 ``` +Notice how we have an excess amount of `DV`. + ## Glossary TODO: From e18876e6bc4ff616e71cbae7b8882dcbe1707d16 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 12:02:51 -0400 Subject: [PATCH 16/20] Add glossary --- docs/spec/auth/vesting.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index cc9990e01a25..26f1a358194f 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -454,4 +454,10 @@ Notice how we have an excess amount of `DV`. ## Glossary -TODO: +- OriginalVesting: The amount of coins (per denomination) that are initially part of a vesting account. These coins are set at genesis. +- StartTime: The BFT time at which a vesting account starts to vest. +- EndTime: The BFT time at which a vesting account is fully vested. +- DelegatedFree: The tracked amount of coins (per denomination) that are delegated from a vesting account that have been fully vested at time of delegation. +- DelegatedVesting: The tracked amount of coins (per denomination) that are delegated from a vesting account that were vesting at time of delegation. +- ContinuousVestingAccount: A vesting account implementation that vests coins linearly over time. +- DelayedVestingAccount: A vesting account implementation that only fully vests all coins at a given time. From 53ea3c580fd3a92faeed004cd7491ee589849d90 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 12:09:34 -0400 Subject: [PATCH 17/20] Address PR review --- docs/spec/auth/vesting.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 26f1a358194f..d2675bac90f6 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -42,13 +42,12 @@ accounts until those coins have been unlocked. When it comes to governance, it is yet undefined if we want to allow a vesting account to be able to deposit vesting coins into proposals. -In addition, a vesting account vests all of it's coin denominations at the same +In addition, a vesting account vests all of its coin denominations at the same rate. This may be subject to change. -**Note**: A vesting account could have some vesting and non-vesting coins at - -required, the `GenesisAccount` type will need to be updated in order to make -such a distinction. +**Note**: A vesting account could have some vesting and non-vesting coins. To +support such a feature, the `GenesisAccount` type will need to be updated in +order to make such a distinction. ## Vesting Account Types From 0110a1e9009bba1469cdb715d1f502837484abc1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 12:13:48 -0400 Subject: [PATCH 18/20] Add sanity check to GetVestedCoins --- docs/spec/auth/vesting.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index d2675bac90f6..5f18f2ac885a 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -127,6 +127,13 @@ is _vesting_. ```go func (cva ContinuousVestingAccount) GetVestedCoins(b Block) Coins { + // We must handle the case where the start time for a vesting account has + // been set into the future or when the start of the chain is not exactly + // known. + if b.Time < va.StartTime { + return ZeroCoins + } + x := b.Time - va.StartTime y := cva.EndTime - cva.StartTime From e9ba183890b3141b53395593431be5d5954740d3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 12:15:38 -0400 Subject: [PATCH 19/20] Minor variable fix --- docs/spec/auth/vesting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 5f18f2ac885a..baf1e3085439 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -134,7 +134,7 @@ func (cva ContinuousVestingAccount) GetVestedCoins(b Block) Coins { return ZeroCoins } - x := b.Time - va.StartTime + x := b.Time - cva.StartTime y := cva.EndTime - cva.StartTime return cva.OriginalVesting * (x / y) From d9e98ab8723ac478e1ba90d9bdb46f9032b91acc Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 26 Oct 2018 12:16:30 -0400 Subject: [PATCH 20/20] More minor variable fixes --- docs/spec/auth/vesting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index baf1e3085439..b8785619b2aa 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -188,7 +188,7 @@ has fully vested, it can send any of it's vested coins. ```go func (dva DelayedVestingAccount) SpendableCoins() Coins { - bc := cva.GetCoins() + bc := dva.GetCoins() return bc - dva.GetVestingCoins() } ```