Lisk's Dynamic Fee System
In this article we give an overview of the main improvements related to the dynamic fee system introduced in the Network Economics phase of the Lisk Protocol Roadmap. All these changes are formally specified as Lisk Improvement Proposals (LIPs) that have been thoroughly researched and reviewed.
We will start with the motivation of having a dynamic fee system, and how this idea has been shaped for the Lisk ecosystem (LIP 0013 and LIP 0025). We will then cover the new feature of transaction invalidation and how it is done with ordered nonces (LIP 0015). Moreover, we will comment on how the block size and block creation has been adapted to this fee system (LIP 0013 and LIP 0002). Finally, we will briefly introduce a fee estimation algorithm proposed to assist users in the context of this specific fee system (LIP 0016).
Why a Dynamic Fee System is Required
Transaction fees in blockchains set a mechanism to distribute scarce resources, typically block size and network bandwidth. This mechanism defines the appropriate economic incentives for the participants offering (delegates forging blocks in Lisk) or demanding (basic users sending transactions) these network resources.
Ideally, if network participants act selfishly and choose a strategy that maximizes their personal gain, then the mechanism ensures that in such an equilibrium (called Nash equilibrium) the scarce resources are distributed efficiently (in game theory this means that the Price of Anarchy is close to 1).
However, even though we can find interesting studies in this direction, an optimal mechanism for transaction fees is very much an open problem in blockchains.
Originally, Lisk used a static fee system as its mechanism, in which there is a fixed fee per transaction type defined in the Lisk protocol. For example, a user had to pay 0.1 LSK tokens to include a transfer transaction into a block, regardless of the network congestion or block size usage. Despite its simplicity for users and developers, in a fixed-price mechanism resources are not distributed efficiently and important limitations are imposed on the usability and scalability of the Lisk ecosystem.
A good example of this is the situation that occurred at the beginning of 2018, where sending a vote transaction could cost more than 30$, whereas registering a new delegate required to spend up to 700$. It is clear that under those circumstances it was difficult to have an active voting system which is paramount in a healthy DPoS consensus system.
With these considerations, we came up with a dynamic fee system based on first-price auctions (pay what you bid) for block space specific to the Lisk ecosystem requirements. This new dynamic fee system was designed to fulfil the following properties:
- The fee system must stimulate the network usage, in particular it has to encourage people to participate in the voting system. In other words, it should be reasonably easy and cheap to issue transactions, especially voting or unvoting delegates.
- The fee system should be as less vulnerable as possible to spam attacks, dust attacks and namespace attacks.
- The fee system should properly align the economic incentives in the Lisk ecosystem so that users end up paying a fair price for their transactions whereas delegates obtain a reasonable profit by including those transactions in blocks.
In the next sections, we will see the changes and features added to the Lisk ecosystem in order to attain these properties. Note that here we are just giving a comprehensive summary of the features and changes, if you want to learn about the concrete specifications, refer to the LIPs linked above and their respective threads in our Lisk Research forum.
Minimum Fee and Minimum Balance
The main concept introduced by the new fee system for the Lisk protocol is the minimum fee for transactions. This means that a necessary condition for a transaction to be valid is that the transaction spends a minimum amount of LSK tokens as fees.
This minimum fee depends on the type of the transaction and its size, and the transaction issuer has to be aware of this value if they don’t want to send an invalid transaction. They can set a fee greater or equal than the minimum fee for their transaction, but they will see their transaction rejected otherwise. In particular, this minimum fee for a transaction, trs, is given by:
minimumFee(trs) = nameFee(trs) + minFeePerByte × size(trs)
where minimumFee() is given in LSK, minFeePerByte is a constant standing for the minimum fee per byte, given in LSK/byte, size() returns the size of the serialized transaction in bytes and nameFee() returns the namespace fee, given in LSK, whose value depends on the transaction type. Let’s see more in detail the meaning behind these parameters.
Minimum Fee per Byte
The constant minFeePerByte is of great importance to discourage transaction spam attacks in the network and to avoid filling up blocks with meaningless transactions. At the same time, this minimum required fee should be reasonably low, otherwise users would be discouraged from using the network and we would be in a situation similar to a static fee system. Following the computations given in LIP 0013 to solve this trade-off, the value given to this constant in the Lisk protocol is:
minFeePerByte = 0.00001 LSK/byte
Let’s say you want to send your friend 1000 LSK. For this, you are going to create a balance transfer transaction, trs1, using a Lisk wallet with the right parameters (amount, address of your friend, your signature of the transaction, etc). Let’s say that the size of this transaction once it is serialized is 136 bytes. Then, for this transaction to be valid, you need to set a transaction fee of at least:
minimumFee(trs1) = 0.00001 LSK/byte × 136 bytes = 0.00136 LSK
Of course, users will not need to do these calculations by hand, they will be automatically done in the Lisk wallet once the different parameters are set. Notice that for this calculation we have assumed that nameFee(trs1) = 0. In the following subsection we explain the reason behind this.
In the same way that minFeePerByte prevents spam attacks, nameFee prevents namespace attacks where a group of users try to exhaust all the possible names (there is a finite number of them), or simply register the most popular ones with the intention of selling them afterwards. At the moment, the only transaction type that is vulnerable to this issue is the delegate registration transaction. Moreover, registering a delegate implies a commitment to become a forging delegate and to secure the network. Thus, considering these factors, the Lisk protocol defines a constant for this type of transactions as:
nameFee(trs) = 10 LSK
if trs is a delegate registration transaction. All other transaction types currently have no impact on a limited namespace and therefore their namespace fee is 0. Bear also in mind that in the future there may be other transactions in the Lisk ecosystem for which a different nameFee will need to be defined (for example a Blockchain Application registration).
You want to become a delegate and participate in the Lisk network consensus. To do this, first you have to issue a delegate registration transaction, trs2, with the right parameters (name for your delegate, your public key, your signature, etc). The size of this transaction, once all its properties are serialized, is 124 bytes. Then you will need to set a transaction fee of at least:
minimumFee(trs2) = 10 LSK + 1 × 10-5 LSK/byte × 124 bytes = 10.00124 LSK
Assignment of the Fees
So far we have only been talking about the minimum fee for every transaction, and the fact that it has to be paid by the users. But, who receives this fee? What happens if a user sets a fee higher than the minimum one? In Lisk's original protocol, the sum of fees of all transactions included in blocks of that round was split equally among all the delegates that forged at least one block in that round.
With the new fee system this changes significantly. First, the fees are assigned to delegates on a per-block basis, so that delegates have an incentive to include the transactions that are more profitable for them. This also encourages delegates to behave more independently and to not offer side-channel payments (transaction issuers could set a zero or near-zero “official fee” and pay delegates directly via other means).
However, by merely assigning the sum of all transaction fees in a block to the forging delegate, delegates could register as many delegates (or Blockchain Applications, in the future) as they wanted for free. They could even offer delegate registrations to their friends for a discounted price, because by including these transactions in the blocks they forge, they would get the entire fees back.
To solve this issue in the new fee system, the minimum part of every transaction is burnt, i.e., it is not added to any account balance and therefore reduces the overall supply of LSK. The rest of the fee is assigned to the delegate who includes the transaction in their block. Even though a delegate may not receive any fee from the transactions in the block they forge, they still have the incentive to include transactions as the burned fees reduce the overall LSK supply.
In the Appendix C of LIP 0013, you can find a study of the impact of burning the minimum fee on the inflation rate of LSK.
user1 is sending a balance transfer transaction to user2 with a fee of 0.005 LSK. When transaction 1 is included in a block, 0.00364 LSK are added to the account balance of delegate1, who forged the block including transaction 1. The minimum fee of transaction 1, 0.00136 LSK, is burned.
In account-based blockchains as Lisk, we face the issue of the accounts state, i.e., the size of the database containing the information of every account, growing excessively large as the result of spam or malicious usage. There are examples in other blockchains of this behaviour, normally referred to as dust accounts attack. To mitigate this issue, we introduced an extra rule for the validity of transactions.
Basically, this minimum balance rule specifies that for any transaction to be valid, the balance of the affected accounts (after the transaction has been applied) has to be equal or higher than 0.05 LSK. This way, spamming the database by creating useless new accounts becomes expensive.
Transaction Invalidation and Nonces
The new fee system gives users the freedom to spend the amount of LSK they consider appropriate for their transactions (as long as it is equal or larger than the minimum fee). This may lead to the undesired situation where certain users end up waiting too long to see their transactions confirmed if the network is busy and there are other transactions offering higher fees for the forging delegates.
Similar to Bitcoin’s RBF feature, transactions in Lisk waiting in the transaction pool can be invalidated by the same issuer by sending a new transaction with higher fee and the same nonce. Once one of the two transactions is included in the blockchain, the other one becomes invalid as the nonce has already been used.
As its name suggests, the nonce specifies a value that can be used only once in a transaction from a certain account. In Lisk, transactions from the same account have to be included in incremental order with respect to the nonce. In other words, each time a transaction is included in the blockchain, the nonce of the sender’s account increases by 1. Thus, the nonce value counts the number of transactions sent from a given account.
An important part of any dynamic fee system is the block size question. In the original Lisk protocol, the maximum number of transactions (of any type) that a block can contain is limited to 25. This rule, which was put in place for performance reasons in early versions of the protocol, will be removed in order to allow a higher usage and throughput of the network expected with the new fee system.
Hence we changed this protocol rule for a byte-based maximum size, in the same way that Bitcoin has a maximum block size. In particular, this means that if a delegate creates a block whose payload size (the total size the transactions in that block) is larger than 15 KB (15360 bytes), the block will be invalid. This implies a maximum throughput of around 1M transactions per day or around 11.5 transactions per second, assuming that most of the transactions will be balance transfer transactions.
We believe that these figures should be enough for the Lisk mainchain due to the sidechains ecosystem in which most of the user interactions will not need to propagate to the mainchain. However, the size limit may be revisited with the release of the Blockchain Interoperability roadmap phase.
The dynamic fee system also requires defining an algorithm for delegates to choose which transactions from the transaction pool to include into a block. The goal of the delegate naturally is to maximize the fees collected from all transactions in their block while obeying to the 15 KB block size limit. This problem reduces to a well-known combinatorial optimization problem, the Knapsack problem.
We solve the problem efficiently using a standard greedy algorithm that yields a close-to-optimal solution. The greedy algorithm means that delegates "greedily" first pick the transactions with higher fee priority while respecting the order of the nonces per account. The fee priority of a transaction is the fee excess per byte on top of the minimum fee, i.e. the part of the fee in a transaction assigned to the delegate divided by the size of the transaction.
Fee Estimation Algorithm
In the previous section, we have briefly commented on the incentives for a delegate when they fill a block with transactions. In this last section, we will focus on their counterpart, the transaction issuers, i.e., those users who want to have their transaction included in the blockchain. To assist them on choosing appropriate transaction fees, we proposed a fee estimation algorithm.
The concept of this algorithm is simple: Assuming that the delegates include first transactions with higher fee priority, this algorithm suggests the user a fee for their transaction depending on its urgency, i.e., how fast the transaction should be included in a block. This fee suggestion is based on an estimation considering the recent history of the blockchain, i.e., the fee spent by the transactions included in recent blocks. The algorithm can return different fee suggestions depending on the transaction urgency for the user. The three fee suggestions by the fee estimation algorithm are the following:
- Fee for low-urgency transactions: Fee estimation considering the transactions with the lowest fees priority in the past blocks.
- Fee for medium-urgency transactions: Fee estimation considering the transactions with an average of the fee priority in the past blocks.
- Fee for high-urgency transactions: Fee estimation considering the transactions with highest fee priority in the past blocks.
Internally, the algorithm utilizes an exponential moving average where the information in the most recent blocks have most importance for the estimation. It is important to note that the algorithm is conceptually simple and does not attempt to predict future conditions, but assumes that these conditions are correlated to the recent past. Thus, we acknowledge that this algorithm may be improved or even a complete new estimation algorithm could be proposed by the community (for example, an algorithm that also considers the current situation of the transaction pool and feeds it into the estimation).
In general, we would love to see such proposals and projects from the community aimed to improve the Lisk ecosystem. That is why we invite all community members to go to the forum, read the LIPs and give feedback. This blog post is a general overview and all the details are given in the specific LIPs. We are always happy to hear your feedback and will be present on the Lisk Research forum for further scientific discussions on this and other topics.