Handlers simplification
Issue
Currently, the problem lies in having complicated handlers to follow, namely OnEvent
. This handler has multiple paths hidden in several implementations, making it harder to read and account for weight. The OnIdty
is on the same model but with fewer paths.
Current Design
Your implementation currently presents two designs for handlers:
- A loosely coupled approach where the handler is implemented in the runtime, and there is no tight coupling between dependencies.
- A tightly coupled approach where the handler is implemented inside one or more pallets, and dependencies are tightly coupled. These handlers are then assembled in a tuple handler, or in a (super) handler in the runtime that call these handlers.
At the moment, both approaches are used simultaneously, with OnEvent
being implemented loosely in the runtime but also tightly inside the wot
pallet (source).
Solution
There are multiple solutions to the problem:
- Splitting the
OnEvent
into smaller handlers, making them more comprehensible, following the model ofOnNewcert
andOnRemovecert
. - Implementing all handlers in a single implementation in the runtime, avoiding tuple handlers. This will make the pallets more flexible, avoiding tight couplings, but it can be counterintuitive for pallets like
duniter-wot
, where there is already a tight coupling with several pallets.
Note
In !246 (merged), Hugo mentioned decoupling the logic and the manual weight accounting that needs to be done for functions called in handlers and hooks. One way to achieve this is to separate the pallet into internal private functions with ()
or DispatchResult
return type and public external functions with a Weight
return type. I created a small POC crate where the weight can be autogenerated based on an expression or a function of the same argument signature, see example below. So, it is entirely possible to go this way with minimal work if the simplification mentioned above is not enough.
#[derive_weight(Weight::from_parts(10, 10))]
fn do_something(i: u32, j: u32) -> u32 {
i * j
}
assert_eq!(weighted_do_something(1, 2), Weight::from_parts(10, 10));
//
fn weight_for_do_something(i: u32, _j: u32) -> Weight {
if i == 0 {
Weight::from_parts(10, 10)
}
else {
Weight::from_parts(20, 20)
}
}
#[derive_weight(weight_for_do_something)]
fn do_something(i: u32, j: u32) -> u32 {
if i == 0 {
i + j
}
else {
i/j
}
}
assert_eq!(weighted_do_something(0, 2), Weight::from_parts(10, 10));
assert_eq!(weighted_do_something(10, 2), Weight::from_parts(20, 20));
//
#[derive_weight((Weight::zero(), Weight::from_parts(10, 10)))]
fn do_something(i: u32, j: u32) -> Result<(), ()> {
if i == 0 {
Ok(())
} else {
Err(())
}
}
assert_eq!(weighted_do_something(0), weight::from_parts(0, 0));
assert_eq!(weighted_do_something(1), weight::from_parts(10, 10));