github.com/Finschia/finschia-sdk@v0.48.1/docs/spec/fee_distribution/f1_fee_distr.tex (about)

     1  \documentclass[]{article}
     2  \usepackage{hyperref}
     3  
     4  %opening
     5  \title{F1 Fee Distribution Draft-02}
     6  \author{Dev Ojha}
     7  
     8  \begin{document}
     9  
    10  \maketitle
    11  
    12  \begin{abstract}
    13  	In a proof of stake blockchain, validators need to split the rewards gained from transaction fees each block. Furthermore, these fees must be fairly distributed to each of a validator's constituent delegators. They accrue this reward throughout the entire time they are delegated, and they have a special operation to withdraw accrued rewards.
    14  	
    15  	The F1 fee distribution scheme works for any algorithm to split funds between validators each block, with minimal iteration, and the only approximations being due to finite decimal precision. Per block there is a single iteration over the validator set, to enable reward algorithms that differ by validator. No iteration is required to delegate, or withdraw. The state usage is one state update per validator per block, and one state entry per active delegation. It can optionally handle arbitrary inflation schemes, and auto-bonding of rewards.
    16  \end{abstract}
    17  
    18  \section{F1 Fee Distribution}
    19  
    20  \subsection{Context}
    21  In a proof of stake blockchain, each validator has an associated stake.
    22  Transaction fees get rewarded to validators based on the incentive scheme of the underlying proof of stake model.
    23  The fee distribution problem occurs in proof of stake blockchains supporting delegation, as there is a need to distribute a validator's fee rewards to its delegators.
    24  The trivial solution of just giving the rewards to each delegator every block is too expensive to perform on-chain.
    25  So instead fee distribution algorithms have delegators perform a withdraw action, which when performed yields the same total amount of fees as if they had received them at every block. 
    26  
    27  This details F1, an approximation-free, slash-tolerant fee distribution algorithm which allows validator commission-rates, inflation rates, and fee proportions, which can all efficiently change per validator, every block.
    28  The algorithm requires iterating over the bonded validators every block, and withdraws require no iteration.
    29  This is cheap, due to staking logic already requiring iteration over all validators, which causes the expensive state-reads to be cached.
    30  
    31  The key point of how F1 works is that it tracks how much rewards a delegator with 1 stake for a given validator would be entitled to if it had bonded at block 0 until the latest block.
    32  When a delegator bonds at block $b$, the amount of rewards a delegator with 1 stake would have if bonded at block 0 until block $b$ is also persisted to state.
    33  When the delegator withdraws, they receive the difference of these two values.
    34  Since rewards are distributed according to stake-weighting, this amount of rewards can be scaled by the amount of stake a delegator had.
    35  Section 1.2 describes this in more detail, with an argument for it being approximation free. 
    36  Section 2 details how to adapt this algorithm to handle commission rates, slashing, and inflation.
    37  
    38  \subsection{Base algorithm}
    39  In this section, we show that the F1 base algorithm gives each delegator rewards identical to that which they'd receive in the naive and correct fee distribution algorithm that iterated over all delegators every block.
    40  
    41  Even distribution of a validators rewards amongst its validators weighted by stake means the following: 
    42  Suppose a delegator delegates $x$ stake to a validator $v$ at block $h$.
    43  Let the amount of stake the validator has at block $i$ be $s_i$ and the amount of fees they receive at this height be $f_i$.
    44  Then if a delegator contributing $x$ stake decides to withdraw at block $n$, the rewards they receive are 
    45  $$\sum_{i = h}^{n} \frac{x}{s_i}f_i = x \sum_{i = h}^{n} \frac{f_i}{s_i}$$
    46  
    47  Note that $s_i$ does not change every block,
    48  it only changes if the validator gets slashed,
    49  or if any delegator alters the amount they have delegated.
    50  We'll relegate handling of slashes to \autoref{ssec:slashing},
    51  and only consider the case with no slashing here.
    52  We can change the iteration from being over every block, to instead being over the set of blocks between two changes in validator $v$'s total stake.
    53  Let each of these set of blocks be called a period.
    54  A new period begins every time that validator's total stake changes.
    55  Let the total amount of stake for the validator in period $p$ be $n_p$.
    56  Let $T_p$ be the total fees that validator $v$ accrued in period $p$.
    57  Let $h$ be the start of period $p_{init}$, and height $n$ be the end of $p_{final}$.
    58  It follows that 
    59  $$x \sum_{i = h}^{n} \frac{f_i}{s_i} = x \sum_{p = p_{init}}^{p_{final}} \frac{T_p}{n_p}$$
    60  
    61  Let $p_0$ represent the period which begins when the validator first bonds.
    62  The central idea to the F1 model is that at the end of the $k$th period,
    63  the following is stored at a state location indexable by $k$: $\sum_{i=0}^{k}\frac{T_i}{n_i}$.
    64  Let the index of the current period be $f$. 
    65  When a delegator wants to delegate or withdraw their reward, they first create a new entry in state to end the current period.
    66  Then this entry is created using the previous entry as follows:
    67  $$Entry_f = \sum_{i=0}^{f}\frac{T_i}{n_i} = \sum_{i=0}^{f-1}\frac{T_i}{n_i} + \frac{T_f}{n_f} = Entry_{f-1} + \frac{T_f}{n_f}$$
    68  Where $T_f$ is the fees the validator has accrued in period $f$, and $n_f$ is the validators total amount of stake in period $f$.
    69  
    70  The withdrawer's delegation object has the index $k$ for the period which they ended by bonding. (They start receiving rewards for period $k + 1$)
    71  The reward they should receive when withdrawing is:
    72  
    73  $$x \sum_{i = k + 1}^{f} \frac{T_i}{n_i} = x\left(\left(\sum_{i=0}^{f}\frac{T_i}{n_i}\right) - \left(\sum_{i=0}^{k}\frac{T_i}{n_i}\right)\right) = x\left(Entry_f - Entry_k\right)$$
    74  
    75  It is clear from the equations that this payout mechanism maintains correctness, and requires no iterations. It just needed the two state reads for these entries.
    76  
    77  $T_f$ is a separate variable in state for the amount of fees this validator has accrued since the last update to its power.
    78  This variable is incremented at every block by however much fees this validator received that block.
    79  On the update to the validators power, this variable is used to create the entry in state at $f$, and is then reset to 0.
    80  
    81  This fee distribution proposal is agnostic to how all of the blocks fees are divied up between validators.
    82  This creates many nice properties, for example it is possible to only rewarding validators who signed that block.
    83   
    84  \section{Additional add-ons}
    85  \subsection{Commission Rates}
    86  Commission rates are the idea that a validator can take a fixed $x\%$ cut of all of their received fees, before redistributing evenly to the constituent delegators.
    87  This can easily be done as follows:
    88  
    89  In block $h$ a validator receives $f_h$ fees.
    90  Instead of incrementing that validators ``total accrued fees this period variable" by $f_h$, it is instead incremented by $(1 - commission\_rate) * f_p$.
    91  Then $commission\_rate * f_p$ is deposited directly to the validator's account.
    92  This allows for efficient updates to a validator's commission rate every block if desired.
    93  More generally, each validator could have a function which takes their fees as input, and outputs a set of outputs to pay these fees too. (i.e. x\% going to themselves, y\% to delegators, z\% burnt)
    94  
    95  \subsection{Slashing}
    96  \label{ssec:slashing}
    97  Slashing is distinct from withdrawals, since it lowers the stake of all of the delegator's by a fixed percentage.
    98  Since no one is charged gas for slashes, a slash cannot iterate over all delegators.
    99  Thus we can no longer just multiply by $x$ over the difference in stake.
   100  This section describes a simple solution that should suffice for most chains needs. An asymptotically optimal solution is provided in section 2.4.
   101  TODO: Consider removing this section in favor of just using the current section 2.4?
   102  
   103  The solution here is to instead store each period created by a slash in the validators state.
   104  Then when withdrawing, you must iterate over all slashes between when you started and ended.
   105  Suppose you delegated at period $0$, a y\% slash occured at period $2$, and your withdrawal creates period $4$.
   106  Then you receive funds from periods $0$ to $2$ as normal.
   107  The equations for funds you receive for periods $2$ to $4$ now uses $(1 - y)x$ for your stake instead of just $x$ stake.
   108  When there are multiple slashes, you just account for the accumulated slash factor.
   109  
   110  In practice this will not really be an efficiency hit, as the number of slashes is expected to be 0 or 1 for most validators.
   111  Validators that get slashed more will naturally lose their delegators.
   112  A malicious validator that gets itself slashed many times would increase the gas to withdraw linearly, but the economic loss of funds due to the slashes is expected to far out-weigh the extra overhead the honest withdrawer must pay for due to the gas.
   113  (TODO: frame that above sentence in terms of griefing factors, as thats more correct)
   114  
   115  \subsection{Inflation}
   116  Inflation is the idea that we want every staked coin to create more staking tokens as time progresses.
   117  The purpose being to drive down the relative worth of unstaked tokens.
   118  Each block, every staked token should produce $x$ staking tokens as inflation, where $x$ is calculated from a function $inflation$ which takes state and the block information as input.
   119  Let $x_i$ represent the evaluation of $inflation$ in the $i$th block.
   120  The goal of this section is to auto-bond inflation in the fee distribution model without iteration.
   121  This is done by preserving the invariant that every state entry contains the rewards one would have if they had bonded one stake at genesis until that corresponding block.
   122  
   123  In state a variable should be kept for the number of tokens one would have now due to inflation,
   124  given that they bonded one token at genesis.
   125  This is $\prod_{0}^{now} (1 + x_i)$.
   126  Each period now stores this total inflation product along with what it already stores per-period.
   127  
   128  Let $R_i$ be the fee rewards in block $i$, and $n_i$ be the total amount bonded to that validator in that block.
   129  The correct amount of rewards which 1 token at genesis should have now is:
   130  $$Reward(now) = \sum_{i = 0}^{now}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i}$$
   131  The term in the sum is the amount of stake one stake becomes due to inflation, multiplied by the amount of fees per stake.
   132  
   133  Now we cast this into the period frame of view.
   134  Recall that we build the rewards by creating a state entry for the rewards of the previous period, and keeping track of the rewards within this period.
   135  Thus we first define the correct amount of rewards for each successive period, proving correctness of this via induction.
   136  We then show that the state entry that gets efficiently built up block by block is equal to this value for the latest period.
   137  
   138  Let $start, end$ denote the start/end of a period.
   139  
   140  Suppose that $\forall f > 0$, $Reward(end(f))$ is correctly constructed as
   141  $$Reward(end(f)) = Reward(end(f-1)) + \sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$
   142  and that for $f = 0$, $Reward(end(0)) = 0$.
   143  (With period 1 being defined as the period that has the first bond into it)
   144  It must be shown that assuming the supposition $\forall f \leq f_0$, $$Reward(end(f_0 + 1)) = Reward(end(f_0)) + \sum_{i = start(f_0 + 1)}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$
   145  Using the definition of $Reward$, it follows that:
   146  $$\sum_{i = 0}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i} = \sum_{i = 0}^{end(f_0)}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i} + \sum_{i = start(f_0 + 1)}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$
   147  
   148  Since the first summation on the right hand side is $Reward(end(f_0))$, the supposition is proven true.
   149  Consequently, the reward for just period $f$ adjusted for the amount of inflation 1 token at genesis would produce, is:
   150  $$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$
   151  
   152  TODO: make this proof + pre-amble less verbose, and just wrap up into a lemma. 
   153  Maybe just leave this proof or the last part to the reader, since it easily follows from summation bounds.
   154  
   155  Now note that 
   156  $$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i} = \left(\prod_{j = 0}^{end(f - 1)} 1 + x_j \right)\sum_{i = start(f)}^{end(f)}\left(\prod_{j = start(f)}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$
   157  By definition of period, and inflation being applied every block, \\
   158  $n_i = n_{start(f)}\left(\prod_{j = start(f)}^{i} 1 + x_j \right)$. This cancels out the product in the summation, therefore
   159  $$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i} = \left(\prod_{j = 0}^{end(f - 1)} 1 + x_j \right)\frac{\sum_{i = start(f)}^{end(f)}R_i}{n_{start(f)}}$$
   160  
   161  Thus every block, each validator just has to add the total amount of fees (The $R_i$ term) that goes to delegates to some per-period term.
   162  When creating a new period, $n_{start(f)}$ can be cached in state, and the product is already stored in the previous periods state entry.
   163  You then get the next period's $n_{start(f)}$ from the consensus' power entry for this validator. 
   164  This is thus extremely efficient per block.
   165  
   166  When withdrawing, you take the difference as before,
   167  which yields the amount of rewards you would have obtained with $(\prod_0^{begin\ bonding\ period}1 + x)$ stake from the block you began bonding at until now.
   168  $(\prod_0^{begin\ bonding\ period}1 + x)$ is known, since its included in the state entry for when you bonded.
   169  You then divide the entitled fees by $(\prod_0^{begin\ bonding\ period}1 + x)$ to normalize it to being the amount of rewards you're entitled to from 1 stake at that block to now.
   170  Then as before, you multiply by the amount of stake you had initially bonded.
   171  \\TODO: (Does the difference equating to that make sense, or should it be shown explicitly)
   172  \\TODO: Does this need to explain how the originally bonded tokens are refunded, or is that clear?
   173  
   174  The inflation function could vary per block,
   175  and per validator if ever a need rose. 
   176  If the inflation rate is the same for everyone then there can be a single global store for the entries corresponding to the product of inflations.
   177  Inflation creation can trivially be epoched as long as inflation isn't required within the epoch, through changes to the $inflation$ function.
   178  
   179  \subsection{Withdrawing with no iteration over slashes}
   180  Notice that a slash is the same as a negative inflation rate for a validator in one block.
   181  For example a $20\%$ slash is equivalent to a $-20\%$ inflation for a validator in a block.
   182  Given correctness of auto-bonding inflation with different inflation rates per-validator,
   183  it follows that handling slashes can be correctly done by simply subtracting the validators inflation factor in that block to be the negative of the slash factor.
   184  This significantly simplifies the withdrawal procedure.
   185  
   186  \subsection{Auto bonding fees}
   187  TODO: Fill this out. 
   188  Core idea: you use the same mechanism as previously, but you just don't take that optimization with $n_{i}$ and the $n_{start}$ relation.
   189  Fairly simple to do.
   190  
   191  \subsection{Delegation updates}
   192  Updating your delegation amount is equivalent to withdrawing earned rewards and a fully independent new delegation occurring in the same block.
   193  The same applies for redelegation.
   194  From the view of fee distribution, partial redelegation is the same as a delegation update + a new delegation.
   195  
   196  \subsection{Jailing / being kicked out of the validator set}
   197  This basically requires no change.
   198  In each block you only iterate over the currently bonded validators.
   199  So you simply don't update the "total accrued fees this period" variable for jailed / non-bonded validators.
   200  Withdrawing requires \textit{no} special casing here!
   201  
   202  \section{State Requirements}
   203  State entries can be pruned quite effectively.
   204  Suppose for the sake of exposition that there is at most one delegation / withdrawal to a particular validator in any given block.
   205  Then each delegation is responsible for one addition to state.
   206  Only the next period, and this delegator's withdrawal could depend on this entry. Thus once this delegator withdraws, this state entry can be pruned.
   207  For the entry created by the delegator's withdrawal, that is only required by the creation of the next period.
   208  Thus once the next period is created, that withdrawal's period can be deleted.
   209  
   210  This can be easily adapted to the case where there are multiple delegations / withdrawals per block, by maintaining a reference count in each period starting state entry.
   211  
   212  The slash entries for a validator can only be pruned when all of that validator's delegators have their bonding period starting after the slash.
   213  This seems ineffective to keep track of, thus it is not worth it.
   214  Each slash should instead remain in state until the validator unbonds and all delegators have their fees withdrawn.
   215  
   216  \section{Implementers Considerations}
   217  TODO: Convert this section into a proper conclusion
   218  
   219  This is an extremely simple scheme with many nice benefits. 
   220  \begin{itemize}
   221  	\item The overhead per block is a simple iteration over the bonded validator set, which occurs anyway. (Thus it can be implemented ``for-free" with an optimized code-base)
   222  	\item Withdrawing earned fees only requires iterating over slashes since when you bonded. (Which is a negligible iteration)
   223  	\item There are no approximations in any of the calculations. (modulo minor errata resulting from fixed precision decimals used in divisions)
   224  	\item Supports arbitrary inflation models. (Thus could even vary upon block signers)
   225  	\item Supports arbitrary fee distribution amongst the validator set. (Thus can account for things like only online validators get fees, which has important incentivization impacts)
   226  	\item The above two can change on a live chain with no issues.
   227  	\item Validator commission rates can be changed every block
   228  	\item The simplicity of this scheme lends itself well to implementation
   229  \end{itemize}
   230  
   231  Thus this scheme has efficiency improvements, simplicity improvements, and expressiveness improvements over the currently proposed schemes. With a correct fee distribution amongst the validator set, this solves the existing problem where one could withhold their signature for risk-free gain.
   232  
   233  \section{TO DOs}
   234  
   235  \begin{itemize}
   236  	\item A global fee pool can be described.
   237  	\item Mention storage optimization for how to prune slashing entries in the uniform inflation and iteration over slashing case
   238  	\item Add equation numbers
   239  	\item perhaps re-organize so that the no iteration
   240   	\item Section on decimal precision considerations (would unums help?), and mitigating errors in calculation with floats and decimals. -- This probably belongs in a corrollary markdown file in the implementation
   241   	\item Consider indicating that the withdraw action need not be a tx type and could instead happen 'transparently' when more coins are needed, if a chain desired this for UX / p2p efficiency.
   242  \end{itemize}
   243  
   244  
   245  \end{document}