diff --git a/tips/TIP-0039/tip-0039.md b/tips/TIP-0039/tip-0039.md index 591b076a1..14ddb53b6 100644 --- a/tips/TIP-0039/tip-0039.md +++ b/tips/TIP-0039/tip-0039.md @@ -2,8 +2,9 @@ tip: 39 title: Mana for IOTA 2.0 description: Define Mana, Mana dynamics, and other Mana-related concepts in the IOTA 2.0 protocol -author: - Olivia Saa (@oliviasaa) , Roman Overko (@roman1e2f5p8s) , Philipp Gackstatter (@PhilippGackstatter) +author: + Olivia Saa (@oliviasaa) , Roman Overko (@roman1e2f5p8s) , Philipp + Gackstatter (@PhilippGackstatter) discussions-to: TODO status: Draft type: Standards @@ -17,39 +18,44 @@ requires: TIP-42 1. [Summary](#summary) 2. [Motivation](#motivation) 3. [Building Blocks](#building-blocks) - - [Data Types \& Subschema Notation](#data-types--subschema-notation) - - [Protocol Parameters](#protocol-parameters) + - [Data Types \& Subschema Notation](#data-types--subschema-notation) + - [Protocol Parameters](#protocol-parameters) 4. [Mana design](#mana-design) - - [Mana Burn](#mana-burn) - - [Reference Mana Cost](#reference-mana-cost) - - [Mana Decay](#mana-decay) - - [Mana Decay Parameters](#mana-decay-parameters) - - [Mana and fixed point arithmetics](#mana-and-fixed-point-arithmetics) - - [How to use the lookup table](#how-to-use-the-lookup-table) - - [Potential Mana](#potential-mana) - - [Potential Mana Semantic Validation](#potential-mana-semantic-validation) - - [Stored Mana](#stored-mana) - - [Stored Mana Semantic Validation](#stored-mana-semantic-validation) - - [Block Issuance Credit](#block-issuance-credit) - - [Block Issuance Credit Semantic Validation](#block-issuance-credit-semantic-validation) - - [Mana rewards](#mana-rewards) - - [Mana Rewards Semantic Validation](#mana-rewards-semantic-validation) - - [Mana Transaction Validation Rules](#mana-transaction-validation-rules) - - [Lookup Table](#lookup-table) + - [Mana Burn](#mana-burn) + - [Reference Mana Cost](#reference-mana-cost) + - [Mana Decay](#mana-decay) + - [Mana Decay Parameters](#mana-decay-parameters) + - [Mana and fixed point arithmetics](#mana-and-fixed-point-arithmetics) + - [How to use the lookup table](#how-to-use-the-lookup-table) + - [Potential Mana](#potential-mana) + - [Potential Mana Semantic Validation](#potential-mana-semantic-validation) + - [Stored Mana](#stored-mana) + - [Stored Mana Semantic Validation](#stored-mana-semantic-validation) + - [Block Issuance Credit](#block-issuance-credit) + - [Block Issuance Credit Semantic Validation](#block-issuance-credit-semantic-validation) + - [Mana rewards](#mana-rewards) + - [Mana Rewards Semantic Validation](#mana-rewards-semantic-validation) + - [Mana Transaction Validation Rules](#mana-transaction-validation-rules) + - [Lookup Table](#lookup-table) 5. [Copyright](#copyright) # Summary -This document defines the Mana-related concepts and the Mana dynamics in the IOTA 2.0 protocol. -The TIP defines the two types of Mana, namely **Potential Mana** and **Stored Mana,** as well as the -**Block Issuance Credit (BIC)** that Mana can be allotted to. +This document defines the Mana-related concepts and the Mana dynamics in the IOTA 2.0 protocol. The TIP defines the two +types of Mana, namely **Potential Mana** and **Stored Mana,** as well as the **Block Issuance Credit (BIC)** that Mana +can be allotted to. # Motivation -Mana is a resource used to determine the allowed throughput of a user and as a payment form for block issuance. Mana can be obtained by constructive and profitable actions for the network, like token holding or engagement in securing the network. Additionally, owning Mana is necessary to use the network by issuing -blocks, so it plays a core role in the IOTA Tokenomics. The following document gives a high-level overview of -Mana types and its dynamics. Mana is a crucial change introduced in the IOTA 2.0 protocol, next to staking, -delegation and congestion control changes. This document works with the Congestion Control Algorithm with Mana, in which block issuers must allot Mana to their account to increase their Block Issuance Credit balance. In order to issue a block a certain amount of BIC is deducted from the issuer account balance and burned. In order to issue blocks and use all functionalitiy related to Mana, the user is required to have an account, which is introduced in [TIP-42](../TIP-0042/tip-0042.md). +Mana is a resource used to determine the allowed throughput of a user and as a payment form for block issuance. Mana can +be obtained by constructive and profitable actions for the network, like token holding or engagement in securing the +network. Additionally, owning Mana is necessary to use the network by issuing blocks, so it plays a core role in the +IOTA Tokenomics. The following document gives a high-level overview of Mana types and its dynamics. Mana is a crucial +change introduced in the IOTA 2.0 protocol, next to staking, delegation and congestion control changes. This document +works with the Congestion Control Algorithm with Mana, in which block issuers must allot Mana to their account to +increase their Block Issuance Credit balance. In order to issue a block a certain amount of BIC is deducted from the +issuer account balance and burned. In order to issue blocks and use all functionalitiy related to Mana, the user is +required to have an account, which is introduced in [TIP-42](../TIP-0042/tip-0042.md). # Building Blocks @@ -64,78 +70,90 @@ Protocol parameters used throughout this TIP are defined in [TIP-49](../TIP-0049 # Mana Design Mana is a core element of the IOTA incentive scheme. It is a resource that can be obtained through: + - generation by holding IOTA coins; - as a reward for staking IOTA coins; - as a reward for delegating IOTA coins; - or simply received or bought from other IOTA users. It is an essential element of the IOTA protocol, as it is used: + - to determine the allowed throughput of an account in the congestion control; - as a payment for block issuance; - as a core part of the IOTA Tokenomics and incentivization scheme. **Mana** can have different forms, each of which is described in the following sections. -- [**Stored Mana**](#stored-mana) is Mana that is stored in UTXOs and can consequently be moved on the UTXO ledger, which allows for Mana Market development. -- [**Potential Mana**](#potential-mana) is generated by holding IOTA coins. The longer the IOTA coins were unspent, the more Potential Mana is generated. When an output is spent, its Potential Mana is released and can be transitioned to one of its more explicit forms, i.e. Stored Mana or Block Issuance Credits. -- [**Block Issuance Credit**](#block-issuance-credit) (BIC) is the form of Mana that can be used to issue blocks. During a transaction, Stored or Potential Mana can be _allotted_ which moves the Mana off the UTXO ledger and converts it to BIC. Only this form of Mana be used to issue blocks. -- [**Mana Rewards**](#mana-rewards) reward participation in staking for validation and delegating IOTA tokens. The Mana rewarded from these activities is not registered in the UTXO ledger and must be moved to it when claiming rewards as described in [TIP-40](../TIP-0040/tip-0040.md##mana-rewards). + +- [**Stored Mana**](#stored-mana) is Mana that is stored in UTXOs and can consequently be moved on the UTXO ledger, + which allows for Mana Market development. +- [**Potential Mana**](#potential-mana) is generated by holding IOTA coins. The longer the IOTA coins were unspent, the + more Potential Mana is generated. When an output is spent, its Potential Mana is released and can be transitioned to + one of its more explicit forms, i.e. Stored Mana or Block Issuance Credits. +- [**Block Issuance Credit**](#block-issuance-credit) (BIC) is the form of Mana that can be used to issue blocks. During + a transaction, Stored or Potential Mana can be _allotted_ which moves the Mana off the UTXO ledger and converts it to + BIC. Only this form of Mana be used to issue blocks. +- [**Mana Rewards**](#mana-rewards) reward participation in staking for validation and delegating IOTA tokens. The Mana + rewarded from these activities is not registered in the UTXO ledger and must be moved to it when claiming rewards as + described in [TIP-40](../TIP-0040/tip-0040.md##mana-rewards). ## Mana Burn -The **Mana holdings** of a user are the sum of all the Potential Mana and Stored Mana (but not the Mana Rewards) in their Account Output -that they use to issue blocks. According to the congestion control mechanism, during each block issuance, the block issuer needs to burn a -certain amount of Mana dictated by the work score of the block and the Reference Mana Cost (RMC). The Mana burned by the -block is subtracted from the block issuer's BIC balance. Users who overspend Mana (i.e., try to issue more blocks -than their Mana Holdings allow) will have their account locked until their debt is paid. +The **Mana holdings** of a user are the sum of all the Potential Mana and Stored Mana (but not the Mana Rewards) in +their Account Output that they use to issue blocks. According to the congestion control mechanism, during each block +issuance, the block issuer needs to burn a certain amount of Mana dictated by the work score of the block and the +Reference Mana Cost (RMC). The Mana burned by the block is subtracted from the block issuer's BIC balance. Users who +overspend Mana (i.e., try to issue more blocks than their Mana Holdings allow) will have their account locked until +their debt is paid. ### Reference Mana Cost -*Reference Mana Cost (RMC)* is used to decide how much Mana should be burned from BIC by each block in that slot. -RMC is computed according to an algorithm based on recent traffic activity: the algorithm counts the number of -blocks in slots that are *Maximum Committable Age (MCA)* slots in the past so that all nodes agree on that value of -RMC and know this value in advance (before issuing the block). +_Reference Mana Cost (RMC)_ is used to decide how much Mana should be burned from BIC by each block in that slot. RMC is +computed according to an algorithm based on recent traffic activity: the algorithm counts the number of blocks in slots +that are _Maximum Committable Age (MCA)_ slots in the past so that all nodes agree on that value of RMC and know this +value in advance (before issuing the block). From a high-level perspective, the RMC algorithm works as follows: + - If the number of blocks in slot `i - MCA` is larger than a given threshold, then the RMC increases. - Otherwise, if the activity is low, the RMC decreases. -- The rate at which the RMC decreases is higher than its increase to tackle situations where the price suddenly -becomes too large for the majority of users, and activity drops. +- The rate at which the RMC decreases is higher than its increase to tackle situations where the price suddenly becomes + too large for the majority of users, and activity drops. -The RMC update only takes into account the blocks issued by accounts having non-negative BIC balances to avoid -price manipulations by malicious actors. Note that blocks from issuers with negative BIC balances are excluded with -respect to the RMC calculation, but they do make part of the slot commitment. To limit fluctuations in the RMC, -it is recommended to update its value every MCA slot at least. +The RMC update only takes into account the blocks issued by accounts having non-negative BIC balances to avoid price +manipulations by malicious actors. Note that blocks from issuers with negative BIC balances are excluded with respect to +the RMC calculation, but they do make part of the slot commitment. To limit fluctuations in the RMC, it is recommended +to update its value every MCA slot at least. ## Mana Decay -Mana Decay is introduced as a control mechanism of the Mana dynamics, altogether with the rate for the Mana -generation both from IOTA coins and the staking mechanism, which will be discussed in detail in the next sections. -To guarantee non-gameability (e.g., splitting accounts or other behaviors that are not helpful for the system -well-functioning) and the fairness of Mana, the same global decay factor needs -to be applied to all the above-mentioned forms of Mana. - -As the exact formulas in which the decay factor will be applied might differ among the Mana types, it is essential to point -out that all of them are based on the same exponential decay with the same parameter `β`. The Incentives Whitepaper (TODO: Link) -provides the specific formula for the decay function in Appendix A. - -Applying decay for the generation of new Mana and staking rewards is conceptually straightforward since it is done at the time of -storing this Mana. Similarly, applying the decay on the already stored Mana happens during the UTXO spending. In -any of those cases, the node must calculate the decay based on epoch indices, not local times. Every -2Slots Per Epoch Exponent slots, the epoch changes, meaning that every set of consecutive 2Slots Per Epoch Exponent -slots will be in the same epoch. - -The decay parameter `β` (together with the Mana generation parameter `Mana Structure::Generation Rate`) was set so the maximum theoretical Mana in -the system is smaller than 2Bits Count - 1, where `Bits Count` refers to the `Mana Structure::Bits Count` protocol parameter. This means that, even though Mana is stored as a uint64, it effectively uses -less than 64 bits (in the case of BIC, it uses at most `Bits Count + 1` bits, `Bits Count` for the value and one -for the sign). Note that the system will almost certainly never use all the `Bits Count` to store Mana since, for the Mana -supply to get to this value, nobody can spend their Mana for years. In practice, we expect to work with Mana values -way smaller than 2Bits Count - 1; however, we must count on what would be an unreasonably large value in practice -to avoid overflowing of the variables even if with a minimal probability. +Mana Decay is introduced as a control mechanism of the Mana dynamics, altogether with the rate for the Mana generation +both from IOTA coins and the staking mechanism, which will be discussed in detail in the next sections. To guarantee +non-gameability (e.g., splitting accounts or other behaviors that are not helpful for the system well-functioning) and +the fairness of Mana, the same global decay factor needs to be applied to all the above-mentioned forms of Mana. + +As the exact formulas in which the decay factor will be applied might differ among the Mana types, it is essential to +point out that all of them are based on the same exponential decay with the same parameter `β`. The Incentives +Whitepaper (TODO: Link) provides the specific formula for the decay function in Appendix A. + +Applying decay for the generation of new Mana and staking rewards is conceptually straightforward since it is done at +the time of storing this Mana. Similarly, applying the decay on the already stored Mana happens during the UTXO +spending. In any of those cases, the node must calculate the decay based on epoch indices, not local times. Every +2Slots Per Epoch Exponent slots, the epoch changes, meaning that every set of consecutive +2Slots Per Epoch Exponent slots will be in the same epoch. + +The decay parameter `β` (together with the Mana generation parameter `Mana Structure::Generation Rate`) was set so the +maximum theoretical Mana in the system is smaller than 2Bits Count - 1, where `Bits Count` +refers to the `Mana Structure::Bits Count` protocol parameter. This means that, even though Mana is stored as a uint64, +it effectively uses less than 64 bits (in the case of BIC, it uses at most `Bits Count + 1` bits, `Bits Count` for the +value and one for the sign). Note that the system will almost certainly never use all the `Bits Count` to store Mana +since, for the Mana supply to get to this value, nobody can spend their Mana for years. In practice, we expect to work +with Mana values way smaller than 2Bits Count - 1; however, we must count on what would be an +unreasonably large value in practice to avoid overflowing of the variables even if with a minimal probability. ### Mana Decay Parameters -The tables below describe the key parameters used for the Mana decay calculations in the next sections of this TIP. Notice that -the parameters in the first table are only used in the explanations in this TIP, but not in the code. +The tables below describe the key parameters used for the Mana decay calculations in the next sections of this TIP. +Notice that the parameters in the first table are only used in the explanations in this TIP, but not in the code. @@ -162,33 +180,33 @@ the parameters in the first table are only used in the explanations in this TIP,
-Additionally, we use the protocol parameters, as defined in [TIP-49], and `Decay Factors Length`, -defined as the length of the lookup Table `Decay Factors`. +Additionally, we use the protocol parameters, as defined in [TIP-49], and `Decay Factors Length`, defined as the length +of the lookup Table `Decay Factors`. ## Mana and fixed point arithmetics -Floating point operations might lead to inconsistencies among results in different nodes due to the different -rounding behaviors in different architectures. The results of the operations involving floating point variables might -differ by amounts that are considered negligible for the modules that deal with local decisions (as the congestion -control module, for example); however, differences that would be considered negligible by those modules can be -fatal for the transaction validation and slot commitments, leading to forks and other undesirable behaviors in the -system. For that reason, fixed point arithmetics (which do not expose the nodes to these rounding divergencies) -must be used in all the modules that require exact consensus over values. In particular, all the Mana and rewards -calculations have to be done with fixed point arithmetics. - -In the last section of this TIP, we introduce a lookup table that will be used in the rest of this TIP (and possibly -other TIPs), as a tool to perform calculations that would otherwise be done with floating point operations. -Specifically, the [lookup table](#lookup-table) introduced is an integer approximation of -2Decay Factors Exponentexp(-βΔn), for different values of `n` ranging from 1 to `Decay Factors Length`. -For the lookup table in this document, we set `Decay Factors Exponent` = 32, `Slots Per Epoch Exponent` = 13, -`Slot Duration in Seconds` = 10, (which implies that `Δ = 0.002597666159`), and `β = 1/3` per year. +Floating point operations might lead to inconsistencies among results in different nodes due to the different rounding +behaviors in different architectures. The results of the operations involving floating point variables might differ by +amounts that are considered negligible for the modules that deal with local decisions (as the congestion control module, +for example); however, differences that would be considered negligible by those modules can be fatal for the transaction +validation and slot commitments, leading to forks and other undesirable behaviors in the system. For that reason, fixed +point arithmetics (which do not expose the nodes to these rounding divergencies) must be used in all the modules that +require exact consensus over values. In particular, all the Mana and rewards calculations have to be done with fixed +point arithmetics. + +In the last section of this TIP, we introduce a lookup table that will be used in the rest of this TIP (and possibly +other TIPs), as a tool to perform calculations that would otherwise be done with floating point operations. +Specifically, the [lookup table](#lookup-table) introduced is an integer approximation of 2Decay Factors +Exponentexp(-βΔn), for different values of `n` ranging from 1 to `Decay Factors Length`. For the lookup +table in this document, we set `Decay Factors Exponent` = 32, `Slots Per Epoch Exponent` = 13, +`Slot Duration in Seconds` = 10, (which implies that `Δ = 0.002597666159`), and `β = 1/3` per year. ### How to use the lookup table -In this section, we define a function `decay(value, epochIndexDiff)`, that decays an amount of Mana `value` by the -factor relative to `epochIndexDiff` epochs. Note that the table `Decay Factors` defined above only gives the values -of decays factors for up to `Decay Factors Length` epochs. To calculate the decay for larger periods of time, we combine -values defined in the [lookup table](#lookup-table) for other numbers smaller than `Decay Factors Length`, using an +In this section, we define a function `decay(value, epochIndexDiff)`, that decays an amount of Mana `value` by the +factor relative to `epochIndexDiff` epochs. Note that the table `Decay Factors` defined above only gives the values of +decays factors for up to `Decay Factors Length` epochs. To calculate the decay for larger periods of time, we combine +values defined in the [lookup table](#lookup-table) for other numbers smaller than `Decay Factors Length`, using an algorithm that will be defined below. First, we define three auxiliary functions: - Let `upperBits(value, n)` be: given a uint64 integer `value`, returns its upper `n` bits @@ -196,102 +214,125 @@ algorithm that will be defined below. First, we define three auxiliary functions - `MultiplicationAndShift(valueHi, valueLo, multFactor, shiftFactor)`: the goal of this function is to return the result - of a multiplication of a uint64 by a uint32, shifted `shiftFactor` times to the right, whenever this result is smaller - than 264. To do so, it uses the following algorithm: - - Let `valueHi` and `valueLo` be the upper and lower 32 bits of an integer smaller than 264, `multFactor` be an integer smaller than 232, and `shiftFactor` be an integer between 0 and 32. - - Let `>>` be a right shift (thus, `>>n` means shifting to the right `n` times). Analogously, let `<<` be a left shift. - - Then compute `valueHi = valueHi * multFactor`. - - Then compute `valueLo = lowerBits(valueHi, shiftFactor) << (32 - shiftFactor) + (valueLo * multFactor) >> shiftFactor`. + - Let `valueHi` and `valueLo` be the upper and lower 32 bits of an integer smaller than 264, + `multFactor` be an integer smaller than 232, and `shiftFactor` be an integer between 0 + and 32. + - Let `>>` be a right shift (thus, `>>n` means shifting to the right `n` times). Analogously, let `<<` be a left + shift. + - Then compute `valueHi = valueHi * multFactor`. + - Then compute + `valueLo = lowerBits(valueHi, shiftFactor) << (32 - shiftFactor) + (valueLo * multFactor) >> shiftFactor`. - Then compute `valueHi = (valueHi >> shiftFactor) + upperBits(valueLo, 32)`. - Then compute `valueLo = lowerBits(valueLo, 32)`. - Return `valueHi, valueLo`, representing the upper 32 and the lower 32 bits, respectively, of the result. -With the functions above defined, we proceed to define the `decay` function that takes as input a uint64 value `value` -and decays it by the correct decay factor relative to `epochIndexDiff` epochs. OBS: note that here, `Decay Factors(epochIndexDiff)` -denotes the value of the table relative to `epochIndexDiff` epochs, which not necessarily corresponds to the `epochIndexDiff`th entry of the table. +With the functions above defined, we proceed to define the `decay` function that takes as input a uint64 value `value` +and decays it by the correct decay factor relative to `epochIndexDiff` epochs. OBS: note that here, +`Decay Factors(epochIndexDiff)` denotes the value of the table relative to `epochIndexDiff` epochs, which not +necessarily corresponds to the `epochIndexDiff`th entry of the table. -- Let `decay(value, epochIndexDiff)` be: - - If `value == 0` or `epochIndexDiff == 0`, return `value`. +- Let `decay(value, epochIndexDiff)` be: + - If `value == 0` or `epochIndexDiff == 0`, return `value`. - Else: - Let `valueHi = upperBits(value, 32)` and `valueLo = lowerBits(value, 32)`. - - Let `m` and `n` be natural numbers such that `m + n * Decay Factors Length = epochIndexDiff`, and `m < Decay Factors Length`. - - Apply `valueHi, valueLo = MultiplicationAndShift(valueHi, valueLo, Decay Factors(Decay Factors Length), Decay Factors Exponent)` `n` times. - - Apply `valueHi, valueLo = MultiplicationAndShift(valueHi, valueLo, Decay Factors(m), Decay Factors Exponent)` once. - - The function should return `valueHi, valueLo` combined into a single `uint64`, i.e., it returns `valueHi << 32 + valueLo`. - -Other implementations of the functions above are possible; however, one must be careful with the order of -operations, which must be done as defined above. Having a well-defined order is crucial since sequences of -divisions and multiplications with integers might lead to different results when the order is altered. -Example: suppose one needs to calculate 1002 * 99 / 100 *21 / 100. Following the left-to-right order, this -would result in 1002 * 99 / 100 * 21 / 100 = 99198 / 100 * 21 / 100 = 991 * 21 / 100 = 20811 / 100 = 208. -If someone did this operation in a different order, let's say 1002 * 21 / 100 * 99 / 100, the result would be -1002 * 21 / 100 * 99 / 100 = 21042 / 100 * 99 / 100 = 210 * 99 / 100 = 20790 / 100 = 207. + - Let `m` and `n` be natural numbers such that `m + n * Decay Factors Length = epochIndexDiff`, and + `m < Decay Factors Length`. + - Apply + `valueHi, valueLo = MultiplicationAndShift(valueHi, valueLo, Decay Factors(Decay Factors Length), Decay Factors Exponent)` + `n` times. + - Apply `valueHi, valueLo = MultiplicationAndShift(valueHi, valueLo, Decay Factors(m), Decay Factors Exponent)` + once. + - The function should return `valueHi, valueLo` combined into a single `uint64`, i.e., it returns + `valueHi << 32 + valueLo`. + +Other implementations of the functions above are possible; however, one must be careful with the order of operations, +which must be done as defined above. Having a well-defined order is crucial since sequences of divisions and +multiplications with integers might lead to different results when the order is altered. Example: suppose one needs to +calculate 1002 * 99 / 100 *21 / 100. Following the left-to-right order, this would result in 1002 _ 99 / 100 _ 21 / 100 += 99198 / 100 _ 21 / 100 = 991 _ 21 / 100 = 20811 / 100 = 208. If someone did this operation in a different order, let's +say 1002 _ 21 / 100 _ 99 / 100, the result would be 1002 _ 21 / 100 _ 99 / 100 = 21042 / 100 _ 99 / 100 = 210 _ 99 / 100 += 20790 / 100 = 207. ## Potential Mana -*Potential Mana* is form of Mana generated from IOTA coins over time. Every IOTA coin (besides storage deposits) -generates potential Mana. The view on the potential Mana value can be deterministically -derived from the UTXO ledger, based on the UTXO IOTA coin value and the time it was created, which corresponds to the `Creation Slot` of the transaction that created the UTXO. +_Potential Mana_ is form of Mana generated from IOTA coins over time. Every IOTA coin (besides storage deposits) +generates potential Mana. The view on the potential Mana value can be deterministically derived from the UTXO ledger, +based on the UTXO IOTA coin value and the time it was created, which corresponds to the `Creation Slot` of the +transaction that created the UTXO. ### Rationale behind the Potential Mana formulas -We model the potential Mana generated by an output holding `S` IOTA coins as the combination of a fixed generation -per slot `γS` and a decay equivalent to a multiplication by exp(-βΔ) every time an epoch ends. +We model the potential Mana generated by an output holding `S` IOTA coins as the combination of a fixed generation per +slot `γS` and a decay equivalent to a multiplication by exp(-βΔ) every time an epoch ends. ![](./img/slots_potential_mana-2.png) -Let `i` be the epoch of the creation slot and `j` be the epoch of the consumption slot. Let `n=j-i`, and for -the sake of the explanation, assume `n`>1. All the Mana generated in epoch `i` "crosses" n decay boundaries; -thus, it must be decayed `n` times. The Mana generated in epoch `i+1` "crosses" n-1 decay boundaries, so it -must be decayed n-1 times, and so on, until the Mana generated in epoch `j`, which is not decayed at all. -Adding these values, we find the following formulas (where d is the number of slots in an epoch): +Let `i` be the epoch of the creation slot and `j` be the epoch of the consumption slot. Let `n=j-i`, and for the sake of +the explanation, assume `n`>1. All the Mana generated in epoch `i` "crosses" n decay boundaries; thus, it must be +decayed `n` times. The Mana generated in epoch `i+1` "crosses" n-1 decay boundaries, so it must be decayed n-1 times, +and so on, until the Mana generated in epoch `j`, which is not decayed at all. Adding these values, we find the +following formulas (where d is the number of slots in an epoch): -Potential Mana = γSd1 exp(-βΔn) + ΣγSd exp(-βΔi) + γSd2, where the summation is over i = 1,...,n-1. +Potential Mana = γSd1 exp(-βΔn) + ΣγSd exp(-βΔi) + γSd2, where the summation is over +i = 1,...,n-1. Solving the sum, this results: -Potential Mana = γSd1 exp(-βΔn) + γSd2 + γSd exp(-βΔ) (1 - exp(-βΔ(n-1))) / (1-exp(-βΔ)), +Potential Mana = γSd1 exp(-βΔn) + γSd2 + γSd exp(-βΔ) (1 - exp(-βΔ(n-1))) / +(1-exp(-βΔ)), -Analogously, if `n=1`, Potential Mana = γSd1 exp(-βΔ) + γSd2; if `n=0`, Potential Mana = γSδ, -where δ is the difference between the creation and consumption slots. +Analogously, if `n=1`, Potential Mana = γSd1 exp(-βΔ) + γSd2; if `n=0`, +Potential Mana = γSδ, where δ is the difference between the creation and consumption slots. ### Potential Mana formulas with fixed point arithmetics +The formulas found in the last section are the exact formulas for the model proposed. However, we must not use floating +point arithmetics, so these formulas must be adapted for the implementation. We begin by rearranging the formula for +n>1, noticing that we already approximated 2`Decay Factors Exponent` exp(-βΔi) by +`Decay Factors(i)`, where `Decay Factors Exponent` is the precision of the [lookup table](#lookup-table) used. The +parameter `γ` is then represented as `Generation Rate`*2-`Generation Rate Exponent`, and +exp(-βΔ)/(1 - exp(-βΔ)) is approximated by +`Decay Factor Epochs Sum`*2-`Decay Factor Epochs Sum Exponent`. For additional explanations +about these approximations, see the Incentives Whitepaper. -The formulas found in the last section are the exact formulas for the model proposed. However, we must not use -floating point arithmetics, so these formulas must be adapted for the implementation. We begin by rearranging the -formula for n>1, noticing that we already approximated 2`Decay Factors Exponent` exp(-βΔi) by -`Decay Factors(i)`, where `Decay Factors Exponent` is the precision of the [lookup table](#lookup-table) used. -The parameter `γ` is then represented as `Generation Rate`*2-`Generation Rate Exponent`, -and exp(-βΔ)/(1 - exp(-βΔ)) is approximated by `Decay Factor Epochs Sum`*2-`Decay Factor Epochs Sum Exponent`. -For additional explanations about these approximations, see the Incentives Whitepaper. +We begin by defining an auxiliary function `generateMana` that (intuitively) generates Mana without applying any type of +decay (note that we use some of the functions and constants defined in the last sections): -We begin by defining an auxiliary function `generateMana` that (intuitively) generates Mana without applying any type of decay -(note that we use some of the functions and constants defined in the last sections): +- `generateMana(value,slotIndexDiff)` returns the generated mana from holding `value` tokens for `slotIndexDiff` slots, + without applying any decay: _ if `slotIndexDiff` == 0 or `Generation Rate` == 0, the function returns `0` _ otherwise, + it returns `MultiplicationAndShift(value, slotIndexDiff * Generation Rate, Generation Rate Exponent)`. - * `generateMana(value,slotIndexDiff)` returns the generated mana from holding `value` tokens for `slotIndexDiff` slots, -without applying any decay: - * if `slotIndexDiff` == 0 or `Generation Rate` == 0, the function returns `0` - * otherwise, it returns `MultiplicationAndShift(value, slotIndexDiff * Generation Rate, Generation Rate Exponent)`. - -Now we define the function `ManaGenerationWithDecay` that actually calculates the Potential Mana using the formulas +Now we define the function `ManaGenerationWithDecay` that actually calculates the Potential Mana using the formulas defined in the last section (including the decays): - * `ManaGenerationWithDecay(amount, slotIndexCreated, slotIndexTarget)` calculates the generated potential mana by holding - `amount` tokens from `slotIndexCreated` to `slotIndexTarget` and applies the decay to the result: - * If `slotIndexCreated` ≥ `slotIndexTarget`, it returns `0`. - * Otherwise, let `epochIndexCreated` and `epochIndexTarget` be the epochs to which `slotIndexCreated` and `slotIndexTarget` belongs to, respectively. We divide the remaining of the function into three cases: - * If `epochIndexCreated` == `epochIndexTarget`, it returns `generateMana(amount, slotIndexTarget-slotIndexCreated)`. - * If `epochIndexCreated` == `epochIndexTarget`-1, let `SlotsBeforeNextEpoch` be the difference between `slotIndexCreated` and the first slot of epoch `epochIndexCreated+1`, and `SlotsSinceEpochStart` be the difference between `slotIndexTarget` and the last slot of epoch `epochIndexTarget-1`. Then, the function returns `manaDecayed + manaGenerated`, where - * `manaDecayed` = `decay(generateMana(amount, SlotsBeforeNextEpoch), 1)` - * `manaGenerated` = `generateMana(amount, SlotsSinceEpochStart)` - * If `epochIndexCreated` < `epochIndexTarget`-1, let `SlotsBeforeNextEpoch` be the difference between `slotIndexCreated` and the first slot of epoch `epochIndexCreated+1`, `SlotsSinceEpochStart` be the difference between `slotIndexTarget` and the last slot of epoch `epochIndexTarget-1`, and `epochIndexDiff` be `epochIndexTarget - epochIndexCreated`. Then, the function returns `potentialMana_0 - potentialMana_n_1 + potentialMana_n`, where - * `potentialMana_n = decay(generateMana(amount, SlotsBeforeNextEpoch), epochIndexDiff)` - * `potentialMana_n_1 = decay(c, epochIndexDiff-1)` - * `potentialMana_0 = c + generateMana(amount, SlotsSinceEpochStart) - (c >> Decay Factors Exponent)` - * `c = MultiplicationAndShift(upperBits(amount, 32), lowerBits(amount, 32), Decay Factor Epochs Sum * Generation Rate , Decay Factor Epochs Sum Exponent+generationRateExponent-slotsPerEpochExponent)`. +- `ManaGenerationWithDecay(amount, slotIndexCreated, slotIndexTarget)` calculates the generated potential mana by + holding `amount` tokens from `slotIndexCreated` to `slotIndexTarget` and applies the decay to the result: + - If `slotIndexCreated` ≥ `slotIndexTarget`, it returns `0`. + - Otherwise, let `epochIndexCreated` and `epochIndexTarget` be the epochs to which `slotIndexCreated` and + `slotIndexTarget` belongs to, respectively. We divide the remaining of the function into three cases: + - If `epochIndexCreated` == `epochIndexTarget`, it returns + `generateMana(amount, slotIndexTarget-slotIndexCreated)`. + - If `epochIndexCreated` == `epochIndexTarget`-1, let `SlotsBeforeNextEpoch` be the difference between + `slotIndexCreated` and the first slot of epoch `epochIndexCreated+1`, and `SlotsSinceEpochStart` be the difference + between `slotIndexTarget` and the last slot of epoch `epochIndexTarget-1`. Then, the function returns + `manaDecayed + manaGenerated`, where + - `manaDecayed` = `decay(generateMana(amount, SlotsBeforeNextEpoch), 1)` + - `manaGenerated` = `generateMana(amount, SlotsSinceEpochStart)` + - If `epochIndexCreated` < `epochIndexTarget`-1, let `SlotsBeforeNextEpoch` be the difference between + `slotIndexCreated` and the first slot of epoch `epochIndexCreated+1`, `SlotsSinceEpochStart` be the difference + between `slotIndexTarget` and the last slot of epoch `epochIndexTarget-1`, and `epochIndexDiff` be + `epochIndexTarget - epochIndexCreated`. Then, the function returns + `potentialMana_0 - potentialMana_n_1 + potentialMana_n`, where + - `potentialMana_n = decay(generateMana(amount, SlotsBeforeNextEpoch), epochIndexDiff)` + - `potentialMana_n_1 = decay(c, epochIndexDiff-1)` + - `potentialMana_0 = c + generateMana(amount, SlotsSinceEpochStart) - (c >> Decay Factors Exponent)` + - `c = MultiplicationAndShift(upperBits(amount, 32), lowerBits(amount, 32), Decay Factor Epochs Sum * Generation Rate , Decay Factor Epochs Sum Exponent+generationRateExponent-slotsPerEpochExponent)`. ### Potential Mana Semantic Validation -- The exact algorithm to calculate the Potential Mana generated by an output created at slot `slotIndexCreated` and consumed at slot `slotIndexTarget` (which has an epoch `consumptionEpoch`), holding `amount` IOTA coins, is given by `ManaGenerationWithDecay(amount, slotIndexCreated, slotIndexTarget)`, where +- The exact algorithm to calculate the Potential Mana generated by an output created at slot `slotIndexCreated` and +consumed at slot `slotIndexTarget` (which has an epoch `consumptionEpoch`), holding `amount` IOTA coins, is given by +`ManaGenerationWithDecay(amount, slotIndexCreated, slotIndexTarget)`, where @@ -323,171 +364,164 @@ defined in the last section (including the decays): ## Stored Mana -*Stored Mana* is the main form of Mana. It is a UTXO-based form (i.e., it is contained in outputs, just like IOTA -tokens) of Mana that can be transferred within certain rules between different users, which allows for Mana -market development. +_Stored Mana_ is the main form of Mana. It is a UTXO-based form (i.e., it is contained in outputs, just like IOTA +tokens) of Mana that can be transferred within certain rules between different users, which allows for Mana market +development. -All output types, except Foundry and Delegation Outputs, can hold stored Mana in the *Mana* field. This field represents the -amount of stored Mana at the output's creation time until the time when the output is consumed without decay applied to it. +All output types, except Foundry and Delegation Outputs, can hold stored Mana in the _Mana_ field. This field represents +the amount of stored Mana at the output's creation time until the time when the output is consumed without decay applied +to it. -Decay must, therefore, be applied whenever an output containing Stored Mana is consumed. The creation slot of each output with stored Mana. +Decay must, therefore, be applied whenever an output containing Stored Mana is consumed. The creation slot of each +output with stored Mana. ### Stored Mana Semantic Validation -- Stored Mana must be decayed before it is transferred into a new output as follows: - - Let `manaAmount`, `consumptionSlot`, and `creationSlot`, be -
Name
- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
manaAmountuint64 - The amount of stored Mana held by the output. -
creationSlotuint64 - The index of slot in which the output was created. -
consumptionSlotuint64 - The index of slot in which the output is consumed. -
- +- Stored Mana must be decayed before it is transferred into a new output as follows: + - Let `manaAmount`, `consumptionSlot`, and `creationSlot`, be + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
manaAmountuint64 + The amount of stored Mana held by the output. +
creationSlotuint64 + The index of slot in which the output was created. +
consumptionSlotuint64 + The index of slot in which the output is consumed. +
+ The algorithm to decay the stored Mana is as follows: -```go +```go creationEpoch = epoch(creationSlot) consumptionEpoch = epoch(consumptionSlot) if creationEpoch == consumptionEpoch: newManaAmount = manaAmount -else: +else: newManaAmount = decay(manaAmount, consumptionEpoch - creationEpoch) ``` - + ## Block Issuance Credit -*Block Issuance Credit (BIC)* Mana is used for congestion control, i.e., to pay for blocks to be scheduled and -gossiped around the network and seen by everyone. Congestion control manages which blocks should be gossiped to -neighbors based on Mana burned from the block issuance credits by the block. It is important to distinguish -between **blocks** and **transactions**: congestion control only deals with blocks, which are the containers that carry transactions around the network. It does not care about the transactions inside the blocks. +_Block Issuance Credit (BIC)_ Mana is used for congestion control, i.e., to pay for blocks to be scheduled and gossiped +around the network and seen by everyone. Congestion control manages which blocks should be gossiped to neighbors based +on Mana burned from the block issuance credits by the block. It is important to distinguish between **blocks** and +**transactions**: congestion control only deals with blocks, which are the containers that carry transactions around the +network. It does not care about the transactions inside the blocks. -The example below shows a ledger with a conflicting branch (perhaps due to a double spend), so only one branch -(either the set of red or the set of green transactions) can ultimately modify the ledger, not both. -However, all these blocks must be gossiped, whether they contain red or green transactions. Thus, they must pay for -the BIC used to get through schedulers regardless of whether the transaction within modifies the ledger. This is -why BIC cannot be burned by a transaction - it can only be burned by a block. +The example below shows a ledger with a conflicting branch (perhaps due to a double spend), so only one branch (either +the set of red or the set of green transactions) can ultimately modify the ledger, not both. However, all these blocks +must be gossiped, whether they contain red or green transactions. Thus, they must pay for the BIC used to get through +schedulers regardless of whether the transaction within modifies the ledger. This is why BIC cannot be burned by a +transaction - it can only be burned by a block. ![](./img/data_flow_overview.png) -Because a block cannot modify the UTXO ledger but the Mana the block burns must be deducted from the account's BIC balance, the BIC is stored in an accounts ledger. This ledger is a map from an Account ID to block issuer data, which includes the BIC balance, the block issuer keys and when the Block Issuer Feature expires among other data. This separate ledger is partly derived from the Block Issuer Feature in an account in the UTXO ledger. The BIC balance of an account can only be modified in two ways: -- BIC is subtracted from the ledger when the corresponding account issues a block signed by one of its Block Issuer Keys. +Because a block cannot modify the UTXO ledger but the Mana the block burns must be deducted from the account's BIC +balance, the BIC is stored in an accounts ledger. This ledger is a map from an Account ID to block issuer data, which +includes the BIC balance, the block issuer keys and when the Block Issuer Feature expires among other data. This +separate ledger is partly derived from the Block Issuer Feature in an account in the UTXO ledger. The BIC balance of an +account can only be modified in two ways: + +- BIC is subtracted from the ledger when the corresponding account issues a block signed by one of its Block Issuer + Keys. - BIC is increased in the ledger when a transaction allots Mana to that account. -Updates to the BIC balance happen upon slot commitments. Mana Decay is applied to the BIC for the -accounts whose BIC was changed in that slot. +Updates to the BIC balance happen upon slot commitments. Mana Decay is applied to the BIC for the accounts whose BIC was +changed in that slot. ### Block Issuance Credit Semantic Validation - When applying Mana Decay to BIC at the end of a slot, it must be applied as follows: - - Let `BIC_i` be the amount of BIC held by the account at the end of slot `i`. Since all additions of allotted BIC were already decayed in that slot, only the previous value of BIC must be decayed, relative to the last slot it was decayed in, by applying: + - Let `BIC_i` be the amount of BIC held by the account at the end of slot `i`. Since all additions of allotted BIC + were already decayed in that slot, only the previous value of BIC must be decayed, relative to the last slot it was + decayed in, by applying: TODO: This function doesn't take into account the last time the BIC was decayed and the current time. -```go +```go if BIC > 0: new_BIC = decay(BIC, 1) -else: - new_BIC = BIC +else: + new_BIC = BIC ``` ## Mana rewards -*Mana rewards* are used to reward for participation in staking for validation and delegating IOTA coins. Rewarding +_Mana rewards_ are used to reward for participation in staking for validation and delegating IOTA coins. Rewarding validators and delegators with Mana is reasonable as it does not extract value from other IOTA coin holders. ### Mana Rewards Semantic Validation - Mana rewards must be calculated on a per-epoch basis. - Mana rewards must become available for claiming when the last slot of the epoch is committed to. -- Mana rewards must be claimed by creating a transaction that consumes the original outputs that were staked or -delegated. +- Mana rewards must be claimed by creating a transaction that consumes the original outputs that were staked or + delegated. - Mana rewards must be put on outputs as stored Mana on the output side of the transaction. -- The decay must be applied to Mana rewards before they are put on outputs according to the following algorithm: - - Let `reward`, `rewardEpoch`, and `claimingSlotEpoch` be as follows: - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
rewarduint64 - The amount of Mana rewards available to be claimed for staking or delegation for a specific epoch. -
rewardEpochuint64 - The index of the epoch for which Mana rewards are claimed. -
claimingSlotEpochuint64 - The epoch of the slot in which the Mana rewards are claimed. -
- - - Let `n = claimingSlotEpoch - rewardEpoch`. If `n > 0`, with the aid of the [lookup table](#lookup-table) and the algorithm -defined in section [Mana and fixed point arithmetics](#mana-and-fixed-point-arithmetics), we must now decay the -rewards by applying `reward = decay(reward, n)`. - - If `n = 0`, the amount to be put on outputs is the entire `reward`. +- The decay must be applied to Mana rewards before they are put on outputs according to the following algorithm: - Let + `reward`, `rewardEpoch`, and `claimingSlotEpoch` be as follows: + + +
Name TypeDescription
reward uint64 The amount of Mana rewards available to be + claimed for staking or delegation for a specific epoch.
rewardEpoch uint64 The + index of the epoch for which Mana rewards are claimed.
claimingSlotEpoch uint64 The epoch of the slot in which the Mana rewards are claimed.
+ - Let `n = claimingSlotEpoch - rewardEpoch`. If `n > 0`, with the aid of the [lookup table](#lookup-table) and the algorithm + defined in section [Mana and fixed point arithmetics](#mana-and-fixed-point-arithmetics), we must now decay the + rewards by applying `reward = decay(reward, n)`. - If `n = 0`, the amount to be put on outputs is the entire `reward`. ## Mana Transaction Validation Rules -- Let `Total Mana In` be the total amount of Mana on the input side of a transaction, consisting of `Total Potential Mana + Total Stored Mana + Total Mana Rewards`, where: +- Let `Total Mana In` be the total amount of Mana on the input side of a transaction, consisting of + `Total Potential Mana + Total Stored Mana + Total Mana Rewards`, where: - `Transaction Creation Slot` is the `Creation Slot` of the transaction, - `Output Creation Slot` is the `Creation Slot` of the output. - `Total Potential Mana` is the sum of Potential Mana for each input `i`: - - where its Potential Mana is calculcated as `Potential Mana(Generation Amount, Output Creation Slot, Transaction Creation Slot)` according to [Potential Mana](#potential-mana) where: + - where its Potential Mana is calculcated as + `Potential Mana(Generation Amount, Output Creation Slot, Transaction Creation Slot)` according to + [Potential Mana](#potential-mana) where: - `Generation Amount` is defined as `Amount_i - Min Deposit`, if `Min Deposit > Amount_i`, otherwise `0`, where: - `Amount_i` is the `Amount` of `i`, - `Min Deposit` is the minimum necessary storage deposit of `i`. - `Total Stored Mana` is the sum of Stored Mana for each input `i`: - - where its Stored Mana is calculated as `Stored Mana(Stored Mana_i, Output Creation Slot, Transaction Creation Slot)`, where: + - where its Stored Mana is calculated as + `Stored Mana(Stored Mana_i, Output Creation Slot, Transaction Creation Slot)`, where: - `Stored Mana_i` is the `Mana` of `i`. - - `Total Mana Rewards` is the sum of decayed Mana Rewards for each input referenced by a _Reward Input_ in the transaction. - - TODO: We should make a function to calculate the decayed Mana Rewards as well, even if the node usually returns the rewards already in decayed form. -- Let `Total Mana Out` be the total amount of Mana on the output side of a transaction, consisting of `Total Stored Mana + Total Allotted Mana`, where: + - `Total Mana Rewards` is the sum of decayed Mana Rewards for each input referenced by a _Reward Input_ in the + transaction. + - TODO: We should make a function to calculate the decayed Mana Rewards as well, even if the node usually returns + the rewards already in decayed form. +- Let `Total Mana Out` be the total amount of Mana on the output side of a transaction, consisting of + `Total Stored Mana + Total Allotted Mana`, where: - `Total Stored Mana` is the sum of `Mana` in each output. - `Total Allotted Mana` is the sum of `Mana` in each _Allotment_. A transaction is only valid if `Total Mana In >= Total Mana Out`. | :warning: Potential Overflows | -|-------------------------------| - -It is highly recommended to use overflow checks for arithmetic operations when calculating Mana balances to avoid over- and underflows. +| ----------------------------- | -## Lookup Table +It is highly recommended to use overflow checks for arithmetic operations when calculating Mana balances to avoid over- +and underflows. +## Lookup Table
Lookup Table: Decays for Mana in decay index granularity scaled to 232