Ever since the decision was made that gas metering is too much of an overhead for the runtime we accepted the fact that we always need to know the pre-dispatch weight of every extrinsic. This forces developers to write benchmarks for every extrinsic and introduces room for mistakes while writing those benchmarks.
That said, @pepyakin came up with a way to do gas metering with less than 5% overhead. This opens up the possibility to remove pre-dispatch weight from extrinsics and replace it with a computation_limit
parameter that needs to be chosen by the user. We will help choosing the value by providing dry-run functionality.
Imagine how much easier writing a runtime becomes when you don’t need to care about writing benchmarks or doing something non linear by accident and DoSing your chain in the process.
However, there is one obstacle left that I want to address in this post: We are currently adding another dimension to weight: bandwidth
. Essentially this means the contribution of an extrinsic to the PoV. It is planned to determine a pre-dispatch bandwidth
using benchmarks just as we do for computation (that we want to eliminate with gas metering). This prevents our plan of freeing developers from writing benchmarks. However, there is no need to have a pre-dispatch bandwidth
because we can just add a bandwidth_limit
parameter to an extrinsic and do the metering completely dynamic.
While we need to go to great length to make computation/gas metering low overhead (see the linked post by @pepyakin ) it is much easier for bandwidth and we should not force developers to come up with a pre-dispatch worst case for it. What we can do is the following:
- Have every storage item bounded by
MaxEncodedLen
(we are working on this anyways). - The runtime itself dynamically meters storage accesses whenever an item is read: Before reading
MaxEncodedLen
it used to make sure that we can actually do this read with the remaining limit. It is immediately corrected after reading the value to the real size. A yet better solution would be if our trie would allow reading the size without putting the whole value into the witness. But that is not the case right now. - After the extrinsic is applied we correct the
bandwidth
to the actual value with feedback from the block builderpov_len_1 - pov_len_0
. This will always be lower because it accounts for values already in the witness.
In contrast to computation metering we can do the bandwidth metering completely within the parachains runtime: The size of a PoV is objective and will just be rejected by all relay chain validators if it is too big (parachchain’s runtime doesn’t properly regulate it). Computation on the other hand is subjective when measured by time and therefore gas metering needs to be applied by the (relay chain) client.
tl;dr: We have a chance to get rid of per-extrinsic benchmarks and we should seriously think about taking it. We will still need benchmarks for host functions and wasm instructions but that doesn’t concern runtime developers.