github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/auth/spec/05_vesting.md (about)

     1  <!--
     2  order: 6
     3  -->
     4  
     5  # Vesting
     6  
     7  - [Vesting](#vesting)
     8    - [Intro and Requirements](#intro-and-requirements)
     9    - [Vesting Account Types](#vesting-account-types)
    10    - [Vesting Account Specification](#vesting-account-specification)
    11      - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts)
    12        - [Continuously Vesting Accounts](#continuously-vesting-accounts)
    13        - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts)
    14      - [Transferring/Sending](#transferringsending)
    15        - [Keepers/Handlers](#keepershandlers)
    16      - [Delegating](#delegating)
    17        - [Keepers/Handlers](#keepershandlers-1)
    18      - [Undelegating](#undelegating)
    19        - [Keepers/Handlers](#keepershandlers-2)
    20    - [Keepers & Handlers](#keepers--handlers)
    21    - [Genesis Initialization](#genesis-initialization)
    22    - [Examples](#examples)
    23      - [Simple](#simple)
    24      - [Slashing](#slashing)
    25    - [Glossary](#glossary)
    26  
    27  ## Intro and Requirements
    28  
    29  This specification defines the vesting account implementation that is used by
    30  the Cosmos Hub. The requirements for this vesting account is that it should be
    31  initialized during genesis with a starting balance `X` and a vesting end
    32  time `ET`. A vesting account may be initialized with a vesting start time `ST`
    33  and a number of vesting periods `P`. If a vesting start time is included, the
    34  vesting period will not begin until start time is reached. If vesting periods
    35  are included, the vesting will occur over the specified number of periods.
    36  
    37  For all vesting accounts, the owner of the vesting account is able to delegate
    38  and undelegate from validators, however they cannot transfer coins to another
    39  account until those coins are vested. This specification allows for three
    40  different kinds of vesting:
    41  
    42  - Delayed vesting, where all coins are vested once `ET` is reached.
    43  - Continous vesting, where coins begin to vest at `ST` and vest linearly with
    44  respect to time until `ET` is reached
    45  - Periodic vesting, where coins begin to vest at `ST` and vest periodically
    46  according to number of periods and the vesting amount per period.
    47  The number of periods, length per period, and amount per period are
    48  configurable. A periodic vesting account is distinguished from a continuous
    49  vesting account in that coins can be released in staggered tranches. For
    50  example, a periodic vesting account could be used for vesting arrangements
    51  where coins are relased quarterly, yearly, or over any other function of
    52  tokens over time.
    53  
    54  ## Note
    55  
    56  Vesting accounts can be initialized with some vesting and non-vesting coins.
    57  The non-vesting coins would be immediately transferable. The current
    58  specification does not allow for vesting accounts to be created with normal
    59  messages after genesis. All vesting accounts must be created at genesis, or as
    60  part of a manual network upgrade. The current specification only allows
    61  for _unconditional_ vesting (ie. there is no possibility of reaching `ET` and
    62  having coins fail to vest).
    63  
    64  ## Vesting Account Types
    65  
    66  ```go
    67  // VestingAccount defines an interface that any vesting account type must
    68  // implement.
    69  type VestingAccount interface {
    70      Account
    71  
    72      GetVestedCoins(Time)  Coins
    73      GetVestingCoins(Time) Coins
    74  
    75      // Delegation and undelegation accounting that returns the resulting base
    76      // coins amount.
    77      TrackDelegation(Time, Coins)
    78      TrackUndelegation(Coins)
    79  
    80      GetStartTime() int64
    81      GetEndTime()   int64
    82  }
    83  
    84  // BaseVestingAccount implements the VestingAccount interface. It contains all
    85  // the necessary fields needed for any vesting account implementation.
    86  type BaseVestingAccount struct {
    87      BaseAccount
    88  
    89      OriginalVesting  Coins // coins in account upon initialization
    90      DelegatedFree    Coins // coins that are vested and delegated
    91      DelegatedVesting Coins // coins that vesting and delegated
    92  
    93      EndTime  int64 // when the coins become unlocked
    94  }
    95  
    96  // ContinuousVestingAccount implements the VestingAccount interface. It
    97  // continuously vests by unlocking coins linearly with respect to time.
    98  type ContinuousVestingAccount struct {
    99      BaseVestingAccount
   100  
   101      StartTime  int64 // when the coins start to vest
   102  }
   103  
   104  // DelayedVestingAccount implements the VestingAccount interface. It vests all
   105  // coins after a specific time, but non prior. In other words, it keeps them
   106  // locked until a specified time.
   107  type DelayedVestingAccount struct {
   108      BaseVestingAccount
   109  }
   110  
   111  // VestingPeriod defines a length of time and amount of coins that will vest
   112  type Period struct {
   113    Length int64 // length of the period, in seconds
   114    Amount Coins // amount of coins vesting during this period
   115  }
   116  
   117  // Stores all vesting periods passed as part of a PeriodicVestingAccount
   118  type Periods []Period
   119  
   120  // PeriodicVestingAccount implements the VestingAccount interface. It
   121  // periodically vests by unlocking coins during each specified period
   122  type PeriodicVestingAccount struct {
   123    BaseVestingAccount
   124    StartTime int64
   125    Periods Periods // the vesting schedule
   126  }
   127  ```
   128  
   129  In order to facilitate less ad-hoc type checking and assertions and to support
   130  flexibility in account usage, the existing `Account` interface is updated to contain
   131  the following:
   132  
   133  ```go
   134  type Account interface {
   135      // ...
   136  
   137      // Calculates the amount of coins that can be sent to other accounts given
   138      // the current time.
   139      SpendableCoins(Time) Coins
   140  }
   141  ```
   142  
   143  ## Vesting Account Specification
   144  
   145  Given a vesting account, we define the following in the proceeding operations:
   146  
   147  - `OV`: The original vesting coin amount. It is a constant value.
   148  - `V`: The number of `OV` coins that are still _vesting_. It is derived by
   149  `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a
   150  per-block basis.
   151  - `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is
   152  computed on demand and not a per-block basis.
   153  - `DV`: The number of delegated _vesting_ coins. It is a variable value. It is
   154  stored and modified directly in the vesting account.
   155  - `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable
   156  value. It is stored and modified directly in the vesting account.
   157  - `BC`: The number of `OV` coins less any coins that are transferred
   158  (which can be negative or delegated). It is considered to be balance of the
   159  embedded base account. It is stored and modified directly in the vesting account.
   160  
   161  ### Determining Vesting & Vested Amounts
   162  
   163  It is important to note that these values are computed on demand and not on a
   164  mandatory per-block basis (e.g. `BeginBlocker` or `EndBlocker`).
   165  
   166  #### Continuously Vesting Accounts
   167  
   168  To determine the amount of coins that are vested for a given block time `T`, the
   169  following is performed:
   170  
   171  1. Compute `X := T - StartTime`
   172  2. Compute `Y := EndTime - StartTime`
   173  3. Compute `V' := OV * (X / Y)`
   174  4. Compute `V := OV - V'`
   175  
   176  Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`,
   177  is _vesting_.
   178  
   179  ```go
   180  func (cva ContinuousVestingAccount) GetVestedCoins(t Time) Coins {
   181      if t <= cva.StartTime {
   182          // We must handle the case where the start time for a vesting account has
   183          // been set into the future or when the start of the chain is not exactly
   184          // known.
   185          return ZeroCoins
   186      } else if t >= cva.EndTime {
   187          return cva.OriginalVesting
   188      }
   189  
   190      x := t - cva.StartTime
   191      y := cva.EndTime - cva.StartTime
   192  
   193      return cva.OriginalVesting * (x / y)
   194  }
   195  
   196  func (cva ContinuousVestingAccount) GetVestingCoins(t Time) Coins {
   197      return cva.OriginalVesting - cva.GetVestedCoins(t)
   198  }
   199  ```
   200  
   201  ### Periodic Vesting Accounts
   202  
   203  Periodic vesting accounts require calculating the coins released during each
   204  period for a given block time `T`. Note that multiple periods could have passed
   205  when calling `GetVestedCoins`, so we must iterate over each period until the
   206  end of that period is after `T`.
   207  
   208  1. Set `CT := StartTime`
   209  2. Set `V' := 0`
   210  
   211  For each Period P:
   212  
   213    1. Compute `X := T - CT`
   214    2. IF `X >= P.Length`
   215        1. Compute `V' += P.Amount`
   216        2. Compute `CT += P.Length`
   217        3. ELSE break
   218    3. Compute `V := OV - V'`
   219  
   220  ```go
   221  func (pva PeriodicVestingAccount) GetVestedCoins(t Time) Coins {
   222    if t < pva.StartTime {
   223      return ZeroCoins
   224    }
   225    ct := pva.StartTime // The start of the vesting schedule
   226    vested := 0
   227    periods = pva.GetPeriods()
   228    for _, period  := range periods {
   229      if t - ct < period.Length {
   230        break
   231      }
   232      vested += period.Amount
   233      ct += period.Length // increment ct to the start of the next vesting period
   234    }
   235    return vested
   236  }
   237  
   238  func (pva PeriodicVestingAccount) GetVestingCoins(t Time) Coins {
   239      return pva.OriginalVesting - cva.GetVestedCoins(t)
   240  }
   241  ```
   242  
   243  #### Delayed/Discrete Vesting Accounts
   244  
   245  Delayed vesting accounts are easier to reason about as they only have the full
   246  amount vesting up until a certain time, then all the coins become vested (unlocked).
   247  This does not include any unlocked coins the account may have initially.
   248  
   249  ```go
   250  func (dva DelayedVestingAccount) GetVestedCoins(t Time) Coins {
   251      if t >= dva.EndTime {
   252          return dva.OriginalVesting
   253      }
   254  
   255      return ZeroCoins
   256  }
   257  
   258  func (dva DelayedVestingAccount) GetVestingCoins(t Time) Coins {
   259      return dva.OriginalVesting - dva.GetVestedCoins(t)
   260  }
   261  ```
   262  
   263  ### Transferring/Sending
   264  
   265  At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`.
   266  
   267  In other words, a vesting account may transfer the minimum of the base account
   268  balance and the base account balance plus the number of currently delegated
   269  vesting coins less the number of coins vested so far.
   270  
   271  ```go
   272  func (va VestingAccount) SpendableCoins(t Time) Coins {
   273      bc := va.GetCoins()
   274      return min((bc + va.DelegatedVesting) - va.GetVestingCoins(t), bc)
   275  }
   276  ```
   277  
   278  #### Keepers/Handlers
   279  
   280  The corresponding `x/bank` keeper should appropriately handle sending coins
   281  based on if the account is a vesting account or not.
   282  
   283  ```go
   284  func SendCoins(t Time, from Account, to Account, amount Coins) {
   285      bc := from.GetCoins()
   286  
   287      if isVesting(from) {
   288          sc := from.SpendableCoins(t)
   289          assert(amount <= sc)
   290      }
   291  
   292      newCoins := bc - amount
   293      assert(newCoins >= 0)
   294  
   295      from.SetCoins(bc - amount)
   296      to.SetCoins(amount)
   297  
   298      // save accounts...
   299  }
   300  ```
   301  
   302  ### Delegating
   303  
   304  For a vesting account attempting to delegate `D` coins, the following is performed:
   305  
   306  1. Verify `BC >= D > 0`
   307  2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting)
   308  3. Compute `Y := D - X` (portion of `D` that is free)
   309  4. Set `DV += X`
   310  5. Set `DF += Y`
   311  
   312  ```go
   313  func (va VestingAccount) TrackDelegation(t Time, amount Coins) {
   314      x := min(max(va.GetVestingCoins(t) - va.DelegatedVesting, 0), amount)
   315      y := amount - x
   316  
   317      va.DelegatedVesting += x
   318      va.DelegatedFree += y
   319  }
   320  ```
   321  
   322  **Note** `TrackDelegation` only modifies the `DelegatedVesting` and `DelegatedFree`
   323  fields, so upstream callers MUST modify the `Coins` field by subtracting `amount`.
   324  
   325  #### Keepers/Handlers
   326  
   327  ```go
   328  func DelegateCoins(t Time, from Account, amount Coins) {
   329      bc := from.GetCoins()
   330      assert(amount <= bc)
   331  
   332      if isVesting(from) {
   333          from.TrackDelegation(t, amount)
   334      } else {
   335          from.SetCoins(sc - amount)
   336      }
   337  
   338      // save account...
   339  }
   340  ```
   341  
   342  ### Undelegating
   343  
   344  For a vesting account attempting to undelegate `D` coins, the following is performed:
   345  NOTE: `DV < D` and `(DV + DF) < D` may be possible due to quirks in the rounding of
   346  delegation/undelegation logic.
   347  
   348  1. Verify `D > 0`
   349  2. Compute `X := min(DF, D)` (portion of `D` that should become free, prioritizing free coins)
   350  3. Compute `Y := min(DV, D - X)` (portion of `D` that should remain vesting)
   351  4. Set `DF -= X`
   352  5. Set `DV -= Y`
   353  
   354  ```go
   355  func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) {
   356      x := min(cva.DelegatedFree, amount)
   357      y := amount - x
   358  
   359      cva.DelegatedFree -= x
   360      cva.DelegatedVesting -= y
   361  }
   362  ```
   363  
   364  **Note** `TrackUnDelegation` only modifies the `DelegatedVesting` and `DelegatedFree`
   365  fields, so upstream callers MUST modify the `Coins` field by adding `amount`.
   366  
   367  **Note**: If a delegation is slashed, the continuous vesting account will end up
   368  with an excess `DV` amount, even after all its coins have vested. This is because
   369  undelegating free coins are prioritized.
   370  
   371  **Note**: The undelegation (bond refund) amount may exceed the delegated
   372  vesting (bond) amount due to the way undelegation truncates the bond refund,
   373  which can increase the validator's exchange rate (tokens/shares) slightly if the
   374  undelegated tokens are non-integral.
   375  
   376  #### Keepers/Handlers
   377  
   378  ```go
   379  func UndelegateCoins(to Account, amount Coins) {
   380      if isVesting(to) {
   381          if to.DelegatedFree + to.DelegatedVesting >= amount {
   382              to.TrackUndelegation(amount)
   383              // save account ...
   384          }
   385      } else {
   386          AddCoins(to, amount)
   387          // save account...
   388      }
   389  }
   390  ```
   391  
   392  ## Keepers & Handlers
   393  
   394  The `VestingAccount` implementations reside in `x/auth`. However, any keeper in
   395  a module (e.g. staking in `x/staking`) wishing to potentially utilize any vesting
   396  coins, must call explicit methods on the `x/bank` keeper (e.g. `DelegateCoins`)
   397  opposed to `SendCoins` and `SubtractCoins`.
   398  
   399  In addition, the vesting account should also be able to spend any coins it
   400  receives from other users. Thus, the bank module's `MsgSend` handler should
   401  error if a vesting account is trying to send an amount that exceeds their
   402  unlocked coin amount.
   403  
   404  See the above specification for full implementation details.
   405  
   406  ## Genesis Initialization
   407  
   408  To initialize both vesting and non-vesting accounts, the `GenesisAccount` struct will
   409  include new fields: `Vesting`, `StartTime`, and `EndTime`. Accounts meant to be
   410  of type `BaseAccount` or any non-vesting type will have `Vesting = false`. The
   411  genesis initialization logic (e.g. `initFromGenesisState`) will have to parse
   412  and return the correct accounts accordingly based off of these new fields.
   413  
   414  ```go
   415  type GenesisAccount struct {
   416      // ...
   417  
   418      // vesting account fields
   419      OriginalVesting  sdk.Coins `json:"original_vesting"`
   420      DelegatedFree    sdk.Coins `json:"delegated_free"`
   421      DelegatedVesting sdk.Coins `json:"delegated_vesting"`
   422      StartTime        int64     `json:"start_time"`
   423      EndTime          int64     `json:"end_time"`
   424  }
   425  
   426  func ToAccount(gacc GenesisAccount) Account {
   427      bacc := NewBaseAccount(gacc)
   428  
   429      if gacc.OriginalVesting > 0 {
   430          if ga.StartTime != 0 && ga.EndTime != 0 {
   431              // return a continuous vesting account
   432          } else if ga.EndTime != 0 {
   433              // return a delayed vesting account
   434          } else {
   435              // invalid genesis vesting account provided
   436              panic()
   437          }
   438      }
   439  
   440      return bacc
   441  }
   442  ```
   443  
   444  ## Examples
   445  
   446  ### Simple
   447  
   448  Given a continuous vesting account with 10 vesting coins.
   449  
   450  ```
   451  OV = 10
   452  DF = 0
   453  DV = 0
   454  BC = 10
   455  V = 10
   456  V' = 0
   457  ```
   458  
   459  1. Immediately receives 1 coin
   460  
   461      ```
   462      BC = 11
   463      ```
   464  
   465  2. Time passes, 2 coins vest
   466  
   467      ```
   468      V = 8
   469      V' = 2
   470      ```
   471  
   472  3. Delegates 4 coins to validator A
   473  
   474      ```
   475      DV = 4
   476      BC = 7
   477      ```
   478  
   479  4. Sends 3 coins
   480  
   481      ```
   482      BC = 4
   483      ```
   484  
   485  5. More time passes, 2 more coins vest
   486  
   487      ```
   488      V = 6
   489      V' = 4
   490      ```
   491  
   492  6. Sends 2 coins. At this point the account cannot send anymore until further
   493  coins vest or it receives additional coins. It can still however, delegate.
   494  
   495      ```
   496      BC = 2
   497      ```
   498  
   499  ### Slashing
   500  
   501  Same initial starting conditions as the simple example.
   502  
   503  1. Time passes, 5 coins vest
   504  
   505      ```
   506      V = 5
   507      V' = 5
   508      ```
   509  
   510  2. Delegate 5 coins to validator A
   511  
   512      ```
   513      DV = 5
   514      BC = 5
   515      ```
   516  
   517  3. Delegate 5 coins to validator B
   518  
   519      ```
   520      DF = 5
   521      BC = 0
   522      ```
   523  
   524  4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins
   525  5. Undelegate from validator A (2.5 coins)
   526  
   527      ```
   528      DF = 5 - 2.5 = 2.5
   529      BC = 0 + 2.5 = 2.5
   530      ```
   531  
   532  6. Undelegate from validator B (5 coins). The account at this point can only
   533  send 2.5 coins unless it receives more coins or until more coins vest.
   534  It can still however, delegate.
   535  
   536      ```
   537      DV = 5 - 2.5 = 2.5
   538      DF = 2.5 - 2.5 = 0
   539      BC = 2.5 + 5 = 7.5
   540      ```
   541  
   542      Notice how we have an excess amount of `DV`.
   543  
   544  ### Periodic Vesting
   545  
   546  A vesting account is created where 100 tokens will be released over 1 year, with
   547  1/4 of tokens vesting each quarter. The vesting schedule would be as follows:
   548  
   549  ```yaml
   550  Periods:
   551  - amount: 25stake, length: 7884000
   552  - amount: 25stake, length: 7884000
   553  - amount: 25stake, length: 7884000
   554  - amount: 25stake, length: 7884000
   555  ```
   556  
   557  ```
   558  OV = 100
   559  DF = 0
   560  DV = 0
   561  BC = 100
   562  V = 100
   563  V' = 0
   564  ```
   565  
   566  1. Immediately receives 1 coin
   567  
   568      ```
   569      BC = 101
   570      ```
   571  
   572  2. Vesting period 1 passes, 25 coins vest
   573  
   574      ```
   575      V = 75
   576      V' = 25
   577      ```
   578  
   579  3. During vesting period 2, 5 coins are transfered and 5 coins are delegated
   580  
   581      ```
   582      DV = 5
   583      BC = 91
   584      ```
   585  
   586  4. Vesting period 2 passes, 25 coins vest
   587  
   588      ```
   589      V = 50
   590      V' = 50
   591      ```
   592  
   593  ## Glossary
   594  
   595  - OriginalVesting: The amount of coins (per denomination) that are initially
   596  part of a vesting account. These coins are set at genesis.
   597  - StartTime: The BFT time at which a vesting account starts to vest.
   598  - EndTime: The BFT time at which a vesting account is fully vested.
   599  - DelegatedFree: The tracked amount of coins (per denomination) that are
   600  delegated from a vesting account that have been fully vested at time of delegation.
   601  - DelegatedVesting: The tracked amount of coins (per denomination) that are
   602  delegated from a vesting account that were vesting at time of delegation.
   603  - ContinuousVestingAccount: A vesting account implementation that vests coins
   604  linearly over time.
   605  - DelayedVestingAccount: A vesting account implementation that only fully vests
   606  all coins at a given time.
   607  - PeriodicVestingAccount: A vesting account implementation that vests coins
   608  according to a custom vesting schedule.