Skip to content
This repository was archived by the owner on Aug 27, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5efc3f6
rename: weight -> fees
JoshOrndorff Oct 14, 2019
f38ac5e
Braindump
JoshOrndorff Oct 14, 2019
e6fcd72
More brain dump
JoshOrndorff Oct 14, 2019
22bb96b
Merge branch 'source' into joshy-kian-fees
shawntabrizi Oct 16, 2019
3d7cc06
A draft of content by kian
kianenigma Oct 22, 2019
ddcae11
more sutff
kianenigma Oct 24, 2019
b23e753
Format line length, add next steps
shawntabrizi Oct 24, 2019
7657db7
typo
shawntabrizi Oct 24, 2019
b0a967f
Suggested refactor
shawntabrizi Oct 24, 2019
35be2aa
Update sidebar and title
shawntabrizi Oct 24, 2019
aa487f5
Revamped
kianenigma Oct 25, 2019
e3502fa
A lot more docs
kianenigma Oct 25, 2019
7e8a5c6
small fixes
kianenigma Oct 25, 2019
cf98f54
struct -> enaum, and operational doesn't pay length fee.
JoshOrndorff Oct 25, 2019
cd83715
Add some links.
JoshOrndorff Oct 25, 2019
e1e882b
Update docs/conceptual/runtime/weight.md
kianenigma Oct 25, 2019
0b0025b
first pass at cleaning up
joepetrowski Oct 28, 2019
c21b5d5
Apply suggestions from code review
kianenigma Oct 31, 2019
bd470be
minor update
kianenigma Oct 31, 2019
c6c8c89
Upstream.into()
kianenigma Oct 31, 2019
e0757cb
Update docs/conceptual/runtime/weight.md
kianenigma Oct 31, 2019
57701a0
minor restructure
joepetrowski Oct 31, 2019
6eed653
Auto-spacing
shawntabrizi Nov 6, 2019
e93d149
Remove outdated TODO. (link is already added at the end)
JoshOrndorff Nov 6, 2019
e2ce9f8
Revise (slightly) repeated section heading.
JoshOrndorff Nov 6, 2019
06ee0db
some updates
shawntabrizi Nov 6, 2019
352db7d
Finish fix weight
shawntabrizi Nov 6, 2019
1832eed
Merge branch 'joshy-kian-fees' of https://github.com/substrate-develo…
shawntabrizi Nov 6, 2019
b4aac7e
Refactor custom weight
shawntabrizi Nov 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/conceptual/runtime/weight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Transaction Weight
---

A number of resources in any chain can be limited, such as storage or computation. Weights exist to
prevent individual components of the chain from consuming too much of any resource.

Consuming some weights should generally incur a fee. The fee implications of the weight system are
covered in the [Fee Developer document](development/module/fees.md).

## Weight Fundamentals

Weights represent the _limited_ resources of your blockchain, for example computational cycles,
memory, storage, etc. A custom implementation may use complex structures to express this. Substrate
weights are simply a [numeric value](/rustdocs/master/sr_primitives/weights/type.Weight.html).

A weight calculation should always:

- Be computable __ahead of dispatch__. A block producer should be able to examine the weight of a
dispatchable before actually deciding to accept it or not.
- Consume few resources itself. It does not make sense to consume similar resources computing a
transaction's weight as would be spent to execute it. Thus, weight computation should be much
lighter than dispatch.
- Delegate _variable_ resource consumption costs and limitations to the dispatched logic. Weights
are good at representing _fixed_ measurements, whereas logic may not be consistently heavy. The
implementation of the dispatch should take the state of the change into account and manually take
extra fees or bonds or take any other measures to make sure that the transaction is safe.

The [System module](https://substrate.dev/rustdocs/master/srml_system/struct.Module.html) is
responsible for accumulating the weight of each block as it gets executed and making sure that it
does not exceed the limit. The [Transaction Payment
module](https://substrate.dev/rustdocs/master/srml_transaction_payment/index.html) is responsible
for interpreting these weights and deducting fees based upon them. The weighing function is part of
the runtime so it can be upgraded if needed.

## Block Weight and Length Limit

Aside from affecting fees, the main purpose of the weight system is to prevent a block from being
filled with too many transactions. While processing transactions within a block, the System module
accumulates both the total length of the block (sum of encoded transactions in bytes) and the total
weight of the block. If either of these numbers surpass the limits, no further transactions are
accepted in that block. These limits are defined in
[`MaximumBlockLength`](/rustdocs/master/srml_system/trait.Trait.html#associatedtype.MaximumBlockLength)
and
[`MaximumBlockWeight`](/rustdocs/master/srml_system/trait.Trait.html#associatedtype.MaximumBlockLength).

One important note about these limits is that a portion of them are reserved for the `Operational`
dispatch class. This rule applies to both of the limits and the ratio can be found in
[`AvailableBlockRatio`](/rustdocs/master/srml_system/trait.Trait.html#associatedtype.AvailableBlockRatio).

For example, if the block length limit is 1 megabyte and the ratio is set to 80%, all transactions
can fill the first 800 kilobytes of the block while the last 200 can only be filled by the
operational class.

## Next Steps

### Learn More

- Substrate Recipes contains examples of both [custom
weights](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/weights)
and custom
[WeightToFee](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/runtimes/weight-fee-runtime).
- The [srml-example](https://github.com/paritytech/substrate/blob/master/srml/example/src/lib.rs)
module.

### Examples

- See an example of [adding a transaction
weight](https://substrate.dev/recipes/design/econsecurity.html?highlight=weight#assigning-transaction-weights)
to a custom runtime function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an examples section, even if it just links to SRML-Example

### References

- Take a look at the [SRML Transaction Payment
module](https://github.com/paritytech/substrate/blob/master/srml/transaction-payment/src/lib.rs).
- Find info about weights including the `SimpleDispatchInfo` enum in
[weights.rs](https://github.com/paritytech/substrate/blob/master/core/sr-primitives/src/weights.rs).
2 changes: 1 addition & 1 deletion docs/development/front-end/polkadot-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Polkadot-JS

The Polkadot-JS project is a collection of tools, interfaces, and libraries around Polkadot and Substrate.

> **Note**: While the project is named after "Polkadot", know that these tools, interfaces, and libraries are fully compatible with any Substrate based chain.
> **Note:**: While the project is named after "Polkadot", know that these tools, interfaces, and libraries are fully compatible with any Substrate based chain.

## Polkadot-JS API

Expand Down
294 changes: 294 additions & 0 deletions docs/development/module/fees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
---
title: Transaction Fees
---

When transactions are submitted to a blockchain, they are executed by the nodes in the network. To
be economically sustainable, nodes charge a fee to execute a transaction. This fee must be covered
by the sender of the transaction. The cost to execute transactions can vary, so Substrate provides a
flexible mechanism to characterize the minimum cost to include a transaction in a block.

The fee system is heavily linked to the [weight system](conceptual/runtime/weight.md). Make sure to
understand weights before reading this document.

## Default Fees

The default fee system uses the uses _fixed_ weights represented by the following enum:

```rust
pub enum SimpleDispatchInfo {
/// A normal dispatch with fixed weight.
FixedNormal(Weight),
/// A normal dispatch with the maximum weight.
MaxNormal,
/// A normal dispatch with no weight.
FreeNormal,
/// An operational dispatch with fixed weight.
FixedOperational(Weight),
/// An operational dispatch with the maximum weight.
MaxOperational,
/// An operational dispatch with no weight.
FreeOperational,
}
```

This enum groups all dispatches into _normal_ or _operational_ and gives them a fixed weight. This
means that the arguments of a dispatchable function do not affect the weight. A dispatch classified
as _operational_ is exempt from paying both the `base_fee` and the `length_fee`.

### Free Normal

This means that this function has no weight. It will not contribute to block fullness at all, and no
weight-fee is applied.

```rust
#[weight = SimpleDispatchInfo::FreeNormal]
pub fn some_normal_function_light() { noop(); }
```

### Fixed Normal

This function will always have a weight `10`.

```rust
#[weight = SimpleDispatchInfo::FixedNormal(10)]
pub fn some_normal_function_heavy() { some_computation(); }
```

### Fixed Operational

This function will have a fixed weight but can consume the reserved operational portion as well.

```rust
#[weight = SimpleDispatchInfo::FixedOperational(20)]
pub fn mission_critical_function() { some_sudo_op(); }
```

### Default

This function will automatically get `#[weight = SimpleDispatchInfo::default()]`.

```rust
pub fn something_else() { noop(); }
```

> **Note:** Be careful! The default implementation of `SimpleDispatchInfo` resolves to
> `FixedNormal(10_000)`. This is due to how things work in `substrate-node` and the desired
> granularity of substrate. Even if you want to use the `SimpleDispatchInfo`, it is very likely that
> you would want it to have a different `Default`.

## Fee Calculation

The final fee of a dispatch is calculated using the weight of the function and a number of
configurable parameters.

### Inclusion Fee

A transaction fee consists of three parts:

* `base_fee`: A fixed fee that is applied to every transaction. See
[`TransactionBaseFee`](/rustdocs/master/srml_transaction_payment/trait.Trait.html#associatedtype.TransactionBaseFee).
* `length_fee`: A per-byte fee that is multiplied by the length, in bytes, of the encoded
transaction. See
[`TransactionByteFee`](/rustdocs/master/srml_transaction_payment/trait.Trait.html#associatedtype.TransactionByteFee).
* `weight_fee`: A per-weight-unit fee that is multiplied by the weight of the transaction. The
weight of each dispatch is denoted via the flexible `#[weight]` annotation. Knowing the weight, it
must be converted to the `Currency` type. For this, each runtime must define a
[`WeightToFee`](/rustdocs/master/srml_transaction_payment/trait.Trait.html#associatedtype.WeightToFee)
type that makes the conversion. `WeightToFee` must be a struct that implements [`Convert<Weight,
Balance>`](/rustdocs/master/sr_primitives/traits/trait.Convert.html).

Based on the above, the final fee of a dispatchable is:

```
fee =
base_fee +
len(tx) * length_fee +
WeightToFee(weight)
```

This `fee` is known as the "inclusion fee." Even if the extrinsic fails, the signer must pay this
inclusion fee.

### Fee Multiplier

The above formula gives a fee that is always the same for the same input. However, weight can be
dynamic and based on how `WeightToFee` is defined, the final fee can include some degree of
variability. To fulfill this requirement, Substrate provides:

- [`NextFeeMultiplier`](/rustdocs/master/srml_transaction_payment/struct.Module.html#method.next_fee_multiplier):
A multiplier stored in the Transaction Payment module and configurable.
- [`FeeMultiplierUpdate`](/rustdocs/master/srml_transaction_payment/trait.Trait.html#associatedtype.FeeMultiplierUpdate):
A configurable parameter for a runtime to describe how this multiplier can change.

`NextFeeMultiplier` has the type `Fixed64`, which can represent a fixed point number. So, given the
inclusion fee formula above, the final version would be:

```
fee =
base_fee +
len(tx) * length_fee +
WeightToFee(weight)

final_fee = fee * NextFeeMultiplier
```

Updating the `NextFeeMultiplier` has a similar effect as updating `WeightToFee`. The
`FeeMultiplierUpdate` associated type in Transaction Payment module is defined as a
`Convert<Fixed64, Fixed64>`, which should be read:

> "it receives the previous multiplier and returns the next one".

The default update function is inspired by the Polkadot network and implements a targeted adjustment
in which a target saturation level of block weight is defined. If the previous block is more
saturated, then the fees are slightly increased. Similarly, if the previous block has fewer
transactions than the target, fees are decreased by a small amount. More information about this can
be found in the [Web3 research
page](https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees).

### Additional Fees

Inclusion fees don't know anything about the logic of the transaction being executed. That is,
Substrate doesn't care what happens in the transaction, it only cares about the size and weight of
the transaction. The inclusion fee will always be paid by the sender.

It's possible to add fees inside dispatchable functions that are only paid if certain logic paths
are executed. Most likely, this will be if the transaction succeeds. The `transfer` function in the
Balances module, for example, takes a fixed fee for transferring tokens.

It is important to note that if you query the chain for a transaction fee, it will only return the
inclusion fee.

## Custom Fees

You can also define custom fee systems through custom weight functions or inclusion fee functions.

### Custom Weights

Implementing a custom weight calculation function can vary in complexity. Using the
`SimpleDispatchInfo` struct provided by Substrate is the easiest approach.

A weight calculation function must provide two trait implementations:

- [`WeightData<T>`]: To determine the weight of the dispatch.
- [`ClassifyDispatch<T>`]: To determine the class of the dispatch.

Substrate then bundles the output information of the two traits into the [`DispatchInfo`] struct and
provides it by implementing the [`GetDispatchInfo`] for all `Call` variants and opaque extrinsic
types. This is used internally by the System and Executive modules; you probably won't use it.

Both `ClassifyDispatch` and `WeightData` are generic over `T`, which gets resolved into the tuple of
all dispatch arguments except for the origin. To demonstrate, we will craft a struct that calculates
the weight as `m * len(args)` where `m` is a given multiplier and `args` is the concatenated tuple
of all dispatch arguments. Further, the dispatch class is `Operational` if the transaction has more
than 100 bytes of length in arguments.

```rust
use coded::Encode;
use sr_primitives::weights::{DispatchClass, ClassifyDispatch, WeightData}

// self.0 is the multiplier, `m`
struct LenWeight(u32);

// We don't quite know what T is. After all, different dispatches have different arguments, hence
// `T` will be different. All that we care about is that `T` is encodable. That is always true by
// definition. All dispatch arguments are encodable.
impl<T: Encode> WeighData<T> for LenWeight {
fn weigh_data(&self, target: T) -> Weight {
let multiplier = self.0;
let encoded_len = target.encode().len() as u32;
multiplier * encoded_len
}
}

impl<T: Encode> ClassifyDispatch<T> for LenWeight {
fn classify_dispatch(&self, target: T) -> DispatchClass {
let encoded_len = target.encode().len() as u32;
if encoded_len > 100 {
DispatchClass::Operational
} else {
DispatchClass::Normal
}
}
}
```

A weight calculator function can also be coerced to the final type of the argument, instead of
defining it as a vague type that is encodable. `srml-example` contains an example of how to do this.
Just note that, in that case, your code would roughly look like:

```rust
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that we use subheadings and separate out these different kinds of fees.

Then it would show up in table of contents, and would allow direct linking.

Comments can become normal text at this point. I can make this change at a later point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get the suggestion: make the code a subheading, or A simple example of using this enum in your runtime is: a new one?

yeah I don't mind you or anyone applying changes. I did my best to put the content right and if it needs reshaping then it is fine.

struct CustomWeight;
impl WeighData<(&u32, &u64)> for CustomWeight {
fn weigh_data(&self, target: (&u32, &u64)) -> Weight {
...
}
}

// given dispatch:
decl_module! {
fn foo(a: u32, b: u64) { ... }
}
```

This means that `CustomWeight` can only be used in conjunction with a dispatch with a particular
signature `(u32, u64)`, as opposed to `LenWeight`, which can be used with anything because they
don't make any strict assumptions about `<T>`.

### Custom Inclusion Fee

This is an example of how to customize your inclusion fee. You must configure the appropriate
associated types in the respective module.

```rust
use sr_primitives::{traits::Convert, weights::Weight}
// Assume this is the balance type
type Balance = u64;

// Assume we want all the weights to have a `100 + 2 * w` conversion to fees
struct CustomWeightToFee;
impl Convert<Weight, Balance> for CustomWeightToFee {
fn convert(w: Weight) -> Balance {
let a = Balance::from(100);
let b = Balance::from(2);
let w = Balance::from(w);
a + b * w
}
}

parameter_types! {
TransactionBaseFee: Balance = 10;
TransactionByteFee: Balance = 10;
}

impl transaction_payment::Trait {
TransactionBaseFee = TransactionBaseFee;
TransactionByteFee = TransactionByteFee;
WeightToFee = CustomWeightToFee;
// we will get to this one soon enough
FeeMultiplierUpdate = ();
}
```

## Next Steps

The entire logic of fees is encapsulated in `srml-transaction-payment` via a `SignedExtension`.
While this module provides a high degree of flexibility, a user can opt to build their custom
payment module drawing inspiration from Transaction Payment.

### Learn More

- Dedicated [weight documentation](conceptual/runtime/weight.md)
- [Example module](https://github.com/paritytech/substrate/blob/master/srml/example/src/lib.rs)
- [SignedExtension](/rustdocs/master/sr_primitives/traits/trait.SignedExtension.html)

### Examples

Substrate Recipes contains examples of both [custom
weights](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/weights) and
custom
[WeightToFee](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/runtimes/weight-fee-runtime).

### References

- [Web3 Foundation
Research](https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees-and-per-block-transaction-limits)
13 changes: 0 additions & 13 deletions docs/development/module/weight.md

This file was deleted.

Loading