Skip to content

Weight to fee conversion

Benjamin Gallois requested to merge bgallois/duniter-v2s:weight_to_fee into master

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.
Edited by Benjamin Gallois

Merge request reports