Skip to main content

5.3 Mana

5.3.1 Introduction

This section introduces access Mana and consensus Mana and determines which modules use each of these two types of Mana.

Any permissionless system needs a Sybil protection mechanism. In Coordicide, this is done by forcing every node to create a node identity (see also Section 3.3 - Peer Discovery). Since the creation of an arbitrarily large number of identities is not an expensive operation, two Difficult-to-obtain resources are linked to each node identity; we call them access Mana(aMana) and consensus Mana (cMana). Both kinds of Mana can be described as essential resources to multiple parts of the network. They are related to the IOTA token, but are not tokens by themselves and do not interfere with the token balance in any direct way. When a transaction is processed, a certain amount of aMana and cMana—dependent on the amount of IOTAs moved by the transaction—will be pledged to nodes specified in the transaction (their node IDs must be specified as defined in Section 2.2 - Standard Payloads Layout). The access and consensus Mana pledged to each node ID must be stored as an extension of the ledger state. The only way a node can obtain aMana or cMana is to convince some ken holders to pledge to it. Both kinds of Mana provides adequate Sybil protection because they are difficult and costly to be acquired in arbitrarily large amounts.

Access and consensus Mana are used as Sybil protection in different modules, which have different natures and requirements. For this reason, it is natural to use different formulas to calculate the appropriate Mana to each module. Consensus Mana should be seen as the Mana that is responsible for the security of the system, on the other hand, access Mana is used to distribute access to the network during congestion periods.

We give a short overview on how each module uses its associated kind of Mana:

The Mana specification depends on the following specification:

5.3.2. Detailed Design - General

Each transaction must have an accessManaNodeID and a consensusManaNodeID field to determine which node to pledge these two types of Mana. Both of these fields consist of a node ID of the node that will receive each kind of Mana. Access Mana and consensus Mana do not have to be pledged to the same node.

Both kinds of Mana are exponential moving averages (EMA) of the base Manas (specifically, cMana is the EMA of base cMana, and aMana is the EMA of base aMana). An EMA is a type of moving average that places a greater weight and significance on the most recent data points. More precisely, the weighting for older data decreases exponentially in time, however, never reaching zero.

Even though the definition of an EMA should be unique (no matter to which function that you apply it), the general algorithm for the calculation of the EMAs—with the flexibility needed in our case—is not easily implementable. Thus, two different algorithms already customized for each type of Mana are provided in the present document.

We define the following parameters:

  • γ\gamma- decay factor for base aMana.
  • α\alpha- moving average factor for the cMana.
  • β\beta- moving average factor for the aMana.

Additionally, we define four vectors that assign a real value to each node Id: the aMana vector, base aMana vector, cMana vector and base cMana vector. The base aMana vector and the base cMana vector are auxiliary vectors (meaning that they are needed to compute the aMana vector and the cMana vector, even though their values are not directly used in any of the modules). Due to the time dependance of the EMA (and also the base aMana function), the four vectors have a reference time to which they are associated.

The Mana vectors are updated in different occasions. Access Mana pledging happens when a transaction is booked on the ledger state. At the same time, entries of the nodes whose aMana is not being modified during the pledging are updated only with respect to the (possibly) newer time reference. In general, updates only due to time (without aMana pledging) happen whenever a node's aMana is being accessed by an external module (as the congestion control, for instance).

On the other hand, cMana is updated only relatively to the end of each epoch (see Section 4.2 - Timestamps). The reason behind this is that epochs are objective, i.e. all nodes agree on which epoch a certain message (or transaction) belongs to. Thus, since nodes agree (with high probability) about transactions with timestamps older than the end of the epoch plus TIMESTAMP_CUTOFF (see Section 4.2 - Timestamps), they will consequently agree—again, with high probability–about the cMana vector relative to these transactions.

Therefore, one can assume that the nodes in sync will (with high probability) agree on values of cMana relative to the set of transactions that have timestamp older than a fixed epoch in the past. On the other hand, aMana will capture recent fluctuations, but nodes are expected to have slightly different perceptions of this quantity.

5.3.3 Detailed Design - Consensus Mana

The base cMana of a node nodeID at time time is defined as the sum over all unspent outputs at time time of transactions with consensusManaNodeID = nodeID. This means that, when an output is consumed, its cMana pledge is revoked and pledged to a (possibly) different node. See the example below:

Example 1:

Suppose transaction zz was booked and we want to update the base cMana vector accordingly. Additionally, suppose transaction zz, xx and yy pledges cMana to nodes NzN_z, NxN_x and NyN_y, respectively.

CMana

Image 5.3.1: Illustration of outputs being consumed.

The update of the base cMana proceeds as follows:

  1. Add 300 to the base cMana state of the node NzN_z.
  2. Subtract 100 from the base cMana state of the node NxN_x.
  3. Subtract 200 from the base cMana state of the node NyN_y.

If a transaction TiT_i with timestamp tit_i pledges MiM_i cMana to a node ZZ, then the cMana evolution over time relative to this transaction will be given by:

cManaZTi(t)={0, if t<tiMi(1eα(tti)), if tti\text{cMana}_Z^{T_i}(t) = \begin{cases} 0, \text{ if } t< t_i \\ M_i\left(1-e^{-\alpha (t-t_i)}\right), \text{ if } t\geq t_i \\ \end{cases}

The total cMana of a node ZZ will be, then, given by the sum of cManaZTi(t)\text{cMana}_Z^{T_i}(t) among all the transactions TiT_i that pledge cMana to node ZZ. Nevertheless, computing the mana using this equation may be excessively demanding in the case where a node has multiples pledges at different times. For that reason, the cMana shall be computed recursively, updating it based on its last value. This update is customized for three different situations (here, t0t_0 is the reference time of the previous cMana value):

  1. pledging of the cMana relative to a transaction with timestamp smaller than t0t_0.
  2. pledging of the cMana relative to a transaction with timestamp larger than t0t_0.
  3. updating the cMana to a time t1>t0t_1>t_0 without any new cMana pledging.

In the first case, the reference time of the cMana must not be changed, whereas in the second case, the reference time of the cMana must be changed to the timestamp of the transaction. This means that a node must never update the cMana to a point in the past (relatively to the last cMana calculated). The exact update procedure for each of the cases defined above are defined next.

5.3.3.1 Consensus Mana Update Procedure

Pledging the cMana relative to a transaction with timestamp smaller than the last reference time

Suppose that the last reference time is tt and the transaction timestamp is tst-s. The update must be done in two steps, always in the order specified below:

  • a) Base cMana update. Just before updating the base cMana vector we must store it, since the last base cMana state (that we call Old_Base_cMana\text{Old\_Base\_cMana}) and the time relative to the last update are used later for the cMana calculations. This temporary vector can be deleted right after the cMana vector is updated.

    The update of the base cMana state goes as follows: if a new transaction with nn inputs of values x1,x2,,xnx_1, x_2, \ldots, x_n pledges cMana to a node NN, then

    1. we add j=1nxj\sum_{j=1}^{n}x_j to the base cMana of the node NN.
    2. Each input IjI_j corresponds to some UTXO (and, consequently, to some transaction) stored in the ledger state. Then, we locate the node Id (let us say, Nodej\text{Node}_j) to whom the cMana relative to this output was pledged to in the past. Then, we subtract xjx_j from the base cMana state of the node Nodej\text{Node}_j, for each IjI_j, j=1,,nj=1,\dots,n.
  • b) Pledging and revoking cMana. If the base cMana balance of node ii (before we added this transaction) was Old_Base_cMana(Nodei)\text{Old\_Base\_cMana}(\text{Node}_i) and the new base cMana balance (after the addition of this transaction) is Base_cMana(Nodei)\text{Base\_cMana}(\text{Node}_i), then we update the cMana vector adding to all nodes' entry the term:

    (1eαs)[Base_cMana(Nodei)Old_Base_cMana(Nodei)](1-e^{-\alpha s})[\text{Base\_cMana}(\text{Node}_i)-\text{Old\_Base\_cMana}(\text{Node}_i)]

    The term above can be negative—since some nodes have their base cMana revoked—but the resulting cMana must not be. Notice that for most nodes (specifically, for all nodes that did not have their base cMana value changed) the value above is zero.

Pledging the cMana relative to a transaction with timestamp larger than the last reference time

Suppose that the last reference time is tst-s and the transaction timestamp is tt. The update must be done in two steps, always in the order specified below:

  • a) Base cMana update. Just before updating the base cMana vector we must store it, since the last base cMana state (that we call Old_Base_cMana\text{Old\_Base\_cMana}) and the time relative to the last update are used later for the cMana calculations. This temporary vector can be deleted right after the cMana vector is updated.

    The update of the base cMana state goes as follows: if a new transaction with nn inputs of respective values x1,x2,,xnx_1, x_2, \ldots, x_n pledges cMana to a node NN, then

    1. we add j=1nxj\sum_{j=1}^{n}x_j to the base cMana of the node NN.
    2. Each input IjI_j corresponds to some UTXO (and, consequently, to some transaction) stored in the ledger state. Then, we locate the node Id (let us say, Nodej\text{Node}_j) to whom the cMana relative to this output was pledged to in the past. Then, we subtract xjx_j from the base cMana state of the node Nodej\text{Node}_j, for each IjI_j, j=1,,nj=1,\dots,n.
  • b) Updating the cMana with respect to time. Suppose that the outdated cMana is Old_cMana\text{Old\_cMana}. We update all cMana entries as follows:

    cMana(Nodei)=eαsOld_cMana(Nodei)+(1eαs)Old_Base_cMana(Nodei)\text{cMana}(\text{Node}_i)=e^{-\alpha s} \text{Old\_cMana}(\text{Node}_i)+(1-e^{-\alpha s})\text{Old\_Base\_cMana}(\text{Node}_i)

    where α\alpha is the moving average parameter for the cMana.

Updating the cMana to a time larger than the last reference time without any new cMana pledging

Suppose that the last reference time is tst-s, the new one is tt and the last cMana vector is Old_cMana\text{Old\_cMana}. We update all cMana entries as follows:

cMana(Nodei)=eαsOld_cMana(Nodei)+(1eαs)Old_Base_cMana(Nodei)\text{cMana}(\text{Node}_i)=e^{-\alpha s} \text{Old\_cMana}(\text{Node}_i)+(1-e^{-\alpha s})\text{Old\_Base\_cMana}(\text{Node}_i)
where $\alpha$ is the moving average parameter for the cMana.

5.3.3.2 Active Consensus Mana and Epochs

The consensus Mana of a node is only calculated and stored relatively to the end of each epoch (see Section 4.2 - Timestamps). Only the values of the last MAX_STORED_EPOCHS epochs are stored. Thus, if tEt_E is the time of the end of epoch EE, to update the cMana vector from epoch E1E-1 to EE, a node must perform the following algorithm:

  1. Update the cMana with respect to time, as described in section 4.3.3.1.3, from time tE1t_{E-1} to tEt_{E}

  2. For each transaction with timestamp in the interval [tE1,tE)[t_{E-1},t_{E}), perform (as described in section 4.3.3.1.1) the base cMana update and the pledge and revoking of cMana, while the reference time tEt_{E} remains constant.

Additionally, we define the active consensus Mana of a node A in epoch E (i.e., relatively to time tEt_{E}) as (here, cMana\text{cMana} is also relative to time tEt_{E}):

Active_cMana(node A)={cMana(node A), if at least one message from node A with timestamp is in [tE1,tE)0, otherwise\text{Active\_cMana}(\text{node A})=\begin{cases} \text{cMana}(\text{node A}), \text{ if at least one message from node A with timestamp is in }[t_{E-1},t_{E}) \\ 0, \text{ otherwise} \end{cases}

Therefore, even if node node has consensus Mana greater than zero at a certain epoch EE, it can be considered dormant in case it did not issue any message during the same epoch. All nodes that have active consensus Mana in epoch EE will form what we call the active consensus Mana set of that epoch, or ACMS(E).

Both calculations defined above (cMana and active cMana on an epoch EE) can only be carried out when epoch EE is finalized—that is, at least TIMESTAMP_CUTOFF (see Section 4.2 - Timestamps) units of time after tEt_E—to make sure that no more messages belonging to the epoch will appear in the network.

The following data structures and functions must be defined:

  • UpdatecMana(epoch): updates the base cMana and cMana vectors from the end of epoch-1 to epoch, pledging and revoking the base cMana and cMana relative to all the relevant transactions.

  • GetActiveConsensusMana(time): returns a mapping between all known nodes and their active cMana, calculated at the end of the epoch that contains time.

  • ManaRank(lowerMana, upperMana, epoch): returns the node ID of the nodes with active cMana in the interval [lowerMana, upperMana], relative to epoch. Notice that epoch must be, at least, the current epoch minus MAX_STORED_EPOCHS and, at most, the last epoch.

5.3.4 Detailed Design - Access Mana

When an output is consumed and funds are consequently transferred, a certain amount of base aMana—dependent on the amount of funds and the age of the output—will be pledged to a node. This pledge is never revoked, as opposed to base cMana. Nevertheless, the base aMana of all nodes will decay over time, which means that all the calculations for aMana will be slightly different than for cMana. The base aMana at time tt, relative to an output TiT_i (of amount MiM_i) consumed by a transaction with timestamp tst-s and generated by a transaction with timestamp tsδt-s-\delta is, for s,δ>0s,\delta>0

Base_aManaTi(t)=M(1eγδ)eγs, if tts\text{Base\_aMana}^{T_i}(t)= M (1-e^{-\gamma \delta}) e^{-\gamma s}, \text{ if } t\geq t-s

If this same output pledges aMana to a node ZZ, then the aMana evolution over time (again, for s,δ>0s,\delta>0) relative to it will be given by:

aManaZTi(t)={Mi(1eγδ)βeβsβγ(e(βγ)s1), if βγMi(1eγδ)γseγs, if β=γ\text{aMana}_Z^{T_i}(t) = \begin{cases} M_i(1-e^{-\gamma\delta})\dfrac{\beta e^{-\beta s}}{\beta-\gamma}\left(e^{(\beta-\gamma)s}-1\right), \text{ if }\beta\neq \gamma\\ M_i(1-e^{-\gamma\delta})\gamma s e^{-\gamma s}, \text{ if } \beta= \gamma\\ \end{cases}

The base aMana of a node nodeID at time time is defined as the sum of the individual base aMana generated by all already consumed outputs TiT_i of transactions with accessManaNodeID = nodeID and timestamp smaller or equal than time. Nevertheless, as in the cMana case, computing the aMana using this equation can be excessively demanding. For that reason, the aMana shall computed recursively, updating it based on its last value. This update is customized for three different situations (here, t0t_0 is the reference time of the last aMana value):

  1. pledging of the aMana relative to a transaction with timestamp smaller than t0t_0;
  2. pledging of the aMana relative to a transaction with timestamp larger than t0t_0;
  3. updating the aMana to a time t1>t0t_1>t_0 without any new aMana pledging.

In the first case, the reference time of the aMana must not be changed, whereas in the second case, the reference time of the cMana must be changed to the timestamp of the transaction. This means that a node must never update the aMana to a point in the past (relatively to the last aMana calculated). The exact update procedure for each of the cases defined above are defined next.

5.3.4.1 Access Mana Update Procedure

Pledging the aMana relative to a transaction with timestamp smaller than the last reference time

Suppose that the last reference time is tt and the transaction timestamp is tst-s. The update must be done in two steps, always in the order specified below:

  • Base aMana pledging. Suppose that the new transaction consists of mm inputs I1,I2,,ImI_1, I_2, \ldots, I_m of value x1,x2,,xmx_1, x_2, \ldots, x_m—respectively—and pledges aMana to a node NN. We update the base aMana of node NN by adding to its base aMana the value

    d=eγsj=1mxj(1eγδj)d=e^{-\gamma s} \sum_{j=1}^{m}x_j(1-e^{-\gamma \delta_{j}})

    where δj>0\delta_{j}>0 is the difference between tt and the timestamps of the transaction that generated IjI_j and γ\gamma is the decay factor. This value dd has to be temporarily stored, since it will be used in the aMana update.

  • Pledging aMana. We update the aMana vector adding to node NN's entry the term:

    {eγseβs(βγ)eγsβd, if βγ;sβd, if β=γ,\begin{cases} \frac{e^{-\gamma s}-e^{-\beta s}}{(\beta-\gamma)e^{-\gamma s}}\beta d,\text{ if }\beta\neq\gamma;\\ s \beta d,\text{ if }\beta=\gamma,\\ \end{cases}

    where the term dd is the same it was added when updating the base aMana vector.

Pledging the aMana relative to a transaction with timestamp larger than the last reference time

Suppose that the last reference time is tst-s and the transaction timestamp is tt. The update must be done in three steps, always in the order specified below:

  • Base aMana update with respect to time. Suppose that the last base aMana is Old_Base_aMana\text{Old\_Base\_aMana}. We update all base aMana entries as follows:

    Base_aMana(Nodei)=eγsOld_Base_aMana(Nodei)\text{Base\_aMana}(\text{Node}_i) = e^{-\gamma s}\text{Old\_Base\_aMana}(\text{Node}_i)
  • Base aMana pledging. Suppose that the new transaction consists of mm inputs I1,I2,,ImI_1, I_2, \ldots, I_m of value x1,x2,,xmx_1, x_2, \ldots, x_m, respectively, and pledges aMana to a node NN. We update the base aMana of node NN by adding to its base aMana the value

    d=j=1mxj(1eγδj)d=\sum_{j=1}^{m}x_j(1-e^{-\gamma \delta_{j}})

    where δj>0\delta_{j}>0 is the difference between tt and the timestamps of the transaction that generated IjI_j and γ\gamma is the decay factor. This value dd has to be temporarily stored, since it will be used in the aMana update.

  • Updating the aMana with respect to time. We update all aMana entries as follows:

     aMana(Nodei)={eβsOld_aMana(Nodei)+eγseβs(βγ)eγsβBase_aMana(Nodei), if βγ;eβsOld_aMana(Nodei)+sβBase_aMana(Nodei), if β=γ,\text{ aMana}(\text{Node}_i)=\begin{cases} e^{-\beta s} \text{Old\_aMana}(\text{Node}_i)+\frac{e^{-\gamma s}-e^{-\beta s}}{(\beta-\gamma)e^{-\gamma s}} \beta\text{Base\_aMana}(\text{Node}_i),\text{ if }\beta\neq\gamma;\\ e^{-\beta s} \text{Old\_aMana}(\text{Node}_i)+s\beta\text{Base\_aMana}(\text{Node}_i),\text{ if }\beta=\gamma,\\ \end{cases}

    where β\beta is the moving average parameter for the aMana and γ\gamma is the base aMana decay factor. Notice that, here, the value of Base_aMana(Nodei)\text{Base\_aMana(Node}_i) used is the one already updated.

Updating the aMana to a time larger than the last reference time without any new aMana pledging.

Suppose that the last reference time is tst-s and new one is tt. The update must be done in two steps, always in the order specified below:

  • Base aMana update with respect to time. Suppose that the outdated base aMana is Old_Base_aMana\text{Old\_Base\_aMana}. We update the base aMana entries as follows:

    Base_aMana(Nodei)=eγsOld_Base_aMana(Nodei)\text{Base\_aMana}(\text{Node}_i) = e^{-\gamma s}\text{Old\_Base\_aMana}(\text{Node}_i)
  • Updating the aMana with respect to time. We update all the aMana entries as follows:

    aMana(Nodei)={eβsOld_aMana(Nodei)+eγseβs(βγ)eγsβBase_aMana(Nodei), if βγ;eβsOld_aMana(Nodei)+sβBase_aMana(Nodei), if β=γ,\text{aMana}(\text{Node}_i)=\begin{cases} e^{-\beta s} \text{Old\_aMana}(\text{Node}_i)+\frac{e^{-\gamma s}-e^{-\beta s}}{(\beta-\gamma)e^{-\gamma s}} \beta\text{Base\_aMana}(\text{Node}_i),\text{ if }\beta\neq\gamma;\\ e^{-\beta s} \text{Old\_aMana}(\text{Node}_i)+s\beta\text{Base\_aMana}(\text{Node}_i),\text{ if }\beta=\gamma,\\ \end{cases}

    where β\beta is the moving average parameter for the aMana and γ\gamma is the base aMana decay factor. Notice that, here, the value of Base_aMana(Nodei)\text{Base\_aMana(Node}_i) used is the one already updated.

The following data structures and functions must be defined:

  • GetAccessMana(): returns a mapping between all known nodes and their access Mana, calculated at currentTime (which means that the aMana is updated to currentTime when this function is called).

  • UpdateaMana(transaction): whenever a transaction transaction is added to the ledger state, it updates the aMana vector in order to add the aMana relative to transaction and to the (possibly) new reference time.

5.3.5. Initialization

The Mana is an extension of the ledger state, hence its calculation depends on the ledger state perception of the node. Snapshotting is the mechanism that stores older ledger states and prunes unnecessary messages. Together with the ledger state, aMana and cMana vectors (and the reference time relative to them) are also saved, since a certain ledger state reflects a certain aMana and cMana distribution in the network. Thus, when a node joins the network, it will query other nodes to get their snapshot file, containing a aMana Snapshot Vector, a cMana Snapshot Vector, and two ACMS (one for each of the last two snapshotted epochs) that will be used as initialization data.

5.3.6. Algorithm

5.3.6.1. Parameter Values

The following parameters will be used by default, and all nodes must know them. We do not have explicit rules to punish nodes that clearly do not use these parameters, but we expect that they would be eventually ignored, due to other implicit mechanisms. For instance, even if the Mana vectors of a certain node are significantly different from the other nodes' view (causing a divergence from the majority's opinion in the voting protocol), with a very high probability its opinion will not affect the final outcome of the voting. For other modules (like congestion control), a node with a significantly different perception of Mana will be blacklisted and will not harm the network. Thus, the nodes have plenty of incentives to follow the rules.

NameTypeDescriptionObservation
DECAYfloatdecay factor for base aManaCalled γ\gamma in the last sections. For a half life of ~6 hours we need γ=0.001925411min\gamma=0.00192541 \frac{1}{\text{min}}.
C_MANA_EMA_COEFFfloatmoving average factor for the cManaCalled α\alpha in the last sections. Set as the same value as γ\gamma.
A_MANA_EMA_COEFFfloatmoving average factor for the aManaCalled β\beta in the last sections. Set as the same value as γ\gamma.

Table 5.3.2: List of Parameters.

5.3.6.2. Local Variables and Built-in Functions

NameTypeDescription
object.cManaNodenodeIDId of the node to which object's cMana was pledged
object.amountdoubleAmount moved by object
object.aManaNodenodeIDId of the node to which object's aMana was pledged
transaction.inputslist of inputs IDsList of inputs consumed by transaction
transaction.timetimeTimestamp of transaction
nodeslist of nodeIDsList of known nodes.
epoch.finalTimetimeFinal time of epoch
epoch.initialTimetimeInitial time of epoch
epoch.transactionslist of TxIdsSet of transactions with timestamps in the interval [epoch.initialTime,epoch.finalTime)
input.timetimeTimestamp of the transaction that generated the output relative to input

Table 5.3.3: Local Variables and Built-in Functions.

5.3.6.3. Pseudocode - cMana Update

In this section, for the sake of clarity, we introduce an example of code of the functions defined above.

UpdateBasecManaTr(transaction)

The function UpdateBasecManaTr(transaction) updates the vector basecMana, pledging and revoking the base cMana relative to a transaction transaction.

FUNCTION UpdateBasecManaTr(transaction)
basecMana[transaction.cManaNode] = basecMana[transaction.cManaNode]+transaction.amount
FOR input in transaction.inputs
basecMana[input.cManaNode] = basecMana[input.cManaNode]-input.amount

UpdatecManaTime(epoch)

The function UpdatecManaTime(epoch) updates the vector cMana, changing its reference time from the end of epoch-1 to the end of epoch.

FUNCTION UpdatecManaTime(epoch)
n = epoch.finalTime-epoch.initialTime
FOR node in nodes
cMana[node] = exp(-C_MANA_EMA_COEFF*n)*cMana[node]
+(1-exp(-C_MANA_EMA_COEFF*n))*basecMana[node]

UpdatecManaTr(epoch,transaction)

The function UpdatecManaTr(epoch,transaction) updates the vector cMana, pledging and revoking the cMana relative to a transaction transaction.

FUNCTION UpdatecManaTr(epoch,transaction)
n = epoch.finalTime - transaction.time
FOR node in nodes
IF basecMana[node] != basecManaOld[node]
cMana[node] = cMana[node]+(1-(1-C_MANA_EMA_COEFF)**n)*(basecMana[node]-basecManaOld[node])

UpdatecMana(epoch)

The function UpdatecMana(epoch) updates the vectors cMana and basecMana, from the end of epoch-1 to epoch, pledging and revoking the base cMana and cMana relative to all the relevant transactions.

FUNCTION UpdatecMana(epoch):
UpdatecManaTime(epoch)
IF epoch.transactions != NULL:
FOR transaction in epoch.transactions:
basecManaOld = basecMana
UpdateBasecManaTr(transaction)
UpdatecManaTr(epoch,transaction)

5.3.6.4. Pseudocode - aMana Update

In this section, for the sake of clarity, we introduce an example of code of the functions defined above.

UpdateBaseaManaTime(t)

The function UpdateBaseaManaTime(t) updates the vector baseaMana, changing its reference time from lastUpdateTime to t.

FUNCTION UpdateBaseaManaTime(t)
n = t-lastUpdateTime
FOR each node i
baseaMana[i] = baseaMana[i]*exp(-DECAY*n)

UpdateaManaTime(t)

The function UpdateaManaTime(t) updates the vector aMana, changing its reference time from lastUpdateTime to t.

FUNCTION UpdateaManaTime(t)
n = t-lastUpdateTime
IF DECAY != A_MANA_EMA_COEFF
FOR each node i
aMana[i] = exp(-A_MANA_EMA_COEFF*n)*aMana[i]+(1-exp((DECAY-A_MANA_EMA_COEFF)*n))/(A_MANA_EMA_COEFF-DECAY))*A_MANA_EMA_COEFF*baseaMana[i]
ELSE
FOR each node i
aMana[i] = exp(-A_MANA_EMA_COEFF*n)*aMana[i]+ n*A_MANA_EMA_COEFF*baseaMana[i]

UpdateBaseaManaTr(transaction)

The function UpdateBaseaManaTr(t,transaction) updates the vector baseaMana, pledging the base aMana relative to a transaction transaction.

FUNCTION UpdateBaseaManaTr(transaction)
FOR input in transaction.inputs
baseaMana[transaction.aManaNode] = baseaMana[transaction.aManaNode]+exp(-DECAY*(MAX(transaction.time,lastUpdateTime)-transaction.time))*input.amount*(1-exp(-DECAY*(transaction.time-input.time)))

UpdateaManaTr(transaction)

The function UpdateaManaTr(transaction) updates the vector aMana, pledging the aMana relative to a transaction transaction.

FUNCTION UpdateaManaTr(transaction):
n = lastUpdateTime-transaction.time
IF DECAY != A_MANA_EMA_COEFF
aMana[transaction.aManaNode] = aMana[transaction.aManaNode]+(exp(-DECAY*n)-exp(-A_MANA_EMA_COEFF*n))/(A_MANA_EMA_COEFF-DECAY))*A_MANA_EMA_COEFF*(baseaMana[transaction.aManaNode]-baseaManaOld[transaction.aManaNode])
ELSE
aMana[aManaNode(transaction)] = aMana[transaction.aManaNode]+exp(-DECAY*n)*DECAY*A_MANA_EMA_COEFF*(baseaMana[transaction.aManaNode]-baseaManaOld[transaction.aManaNode])

UpdateaMana(transaction)

The function UpdateaMana(transaction) updates the vectors aMana and baseaMana, from lastUpdateTime to MAX(lastUpdateTime, transaction.time), pledging the base aMana and aMana relative to transaction.

FUNCTION UpdateaMana(transaction):
# if the tx is not old, add it and update the vector to t
IF transaction.time > lastUpdateTime:
UpdateBaseaManaTime(transaction.time)
UpdateaManaTime(transaction.time)
UpdateBaseaManaTr(transaction)
lastUpdateTime = transaction.time
# add a transaction in the past
IF transaction.time < lastUpdateTime:
baseaManaOld = baseaMana
UpdateBaseaManaTr(transaction)
UpdateaManaTr(transaction)