Weight to fee conversion
Summary
- Introduce a constant fees model for testing with the
constant-fees
feature. - Implement a non-constant fees model for production (details below).
- Change the naming in
pallet_certification
to enhance documentation generation and improve readability for users. - Resolve benchmark weight files naming.
Fee
Fee paid by the user when executing an extrinsic is computed as:
(base_fee + length_fee + weight_fee) * fee_multiplier
With:
base_fee = weight_to_fee(base_weight)
weight_fee = weight_to_fee(extrinsic_weight)
length_fee = length_to_fee(extrinsic_length_bytes)
-
fee_multiplier
can be adjusted after each block.
Transaction fees should provide an incentive to limit on-chain resource consumption (storage, computation) and avoid malicious attacks.
Choosing parameters
Common guidelines to set these arbitrary functions (multiplier
, weight_to_fee
and length_to_fee
) are:
- How many extrinsics could fit in a block.
- How much people should pay at a minimum to fill a whole block with their transactions.
A fee too low will make the network vulnerable to attacks, and too high will inhibit the incentive to participate in the network. In our case, with the quota system, members will be refunded after each transaction if the network is not saturated.
Duniter-v1
Taking some statistics from the Duniter-v1 network over the past year, we observe that the mean transaction frequency is 0.01, representing 0.06 transactions per block of Duniter-v2s. At peak usage (occurring for less than a week cumulatively throughout the year), there were 0.09 transactions per second, equating to 0.54 transactions per Duniter-v2s block.
The maximum wallet value is 269 029 ĞD, while the median value of non-empty wallets is 450 ĞD. Additionally, only 25% of non-empty wallets have currently a balance exceeding 3 700 ĞD and the DU is currently at 10.78 ĞD.
Duniter-v2
To get an order of magnitude, on the actual reference machine, 26 482 700 base extrinsics can fill a block. The most computationally intensive calls are create_identity
, revoke_membership
, and remove_member
, and will take approximately 2 000 extrinsics to fill the block. Most calls are of the order of magnitude similar to a balance transfer that takes 8 264 calls to fill a block.
First proposition
To start implementing the logic, a good starting point could be:
- Use
WeightToFeePolynomial
with a 1-degree polynomial to map 5 cĞD to 1 base extrinsic weight so that filling a block with base extrinsics will cost 1 324 135 ĞD, and filling a block with the most common extrinsics will cost 1 300 ĞD. - Use 1 cĞD for each 100 bytes of extrinsic lengths (not much vulnerability to attacks with our extrinsics on this side).
-
FeeMultiplier
can be used to account for the variation of demand on the network. As the data from duniter-v1 shows that we are far from overcharging the network, it can be initially set to one, but it can be adjusted in the future to account for monetary mass and user growth.
With these values, a simple balance transfer will cost 17 cĞD (0.016 DU), which is not prohibitively high for legitimate transactions (as it will be refunded). However, a malicious attacker would need to commit (and partially lose) at least 1 400 ĞD (130 DU) to fill one block that is more than 60% of the non-empty wallets in the current network.
This MR will put in place the logic to implement a non-constant weight-to-fee conversion, but the final exact parameters will also need discussion and adjustment, taking into account the final reference machine and the dynamics of the network at deployment.
To do:
-
Weight to fee. -
Length to fee. -
Fix and run overhead benchmark with correct weight to fee. -
Fine-tuning of parameters after discussion. -
Add a deterministic fees model for testing. -
Update tests with deterministic fees model.