-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Grand Central Dispatch #5130
Grand Central Dispatch #5130
Conversation
tomusdrw
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be interesting to see how this glues to the rest by
- putting the docs there for the main trait
- finishing bode/runtime/src/lib.rs
d9f689d to
cfe572b
Compare
|
Rebased this PR onto the current master and fixed the test build. This was quiet a lot of work because the additional The test are successful with the exception of the |
|
That's good news! I think ui tests could be fixed by merging in master again. |
290d119 to
a0cb883
Compare
It was just the missing documentation warning that made the UI test fail. Fixed that now. CI is finally happy and we are in sync with the current master. The exception is the check-runtime CI which we should ignore. |
I added some documentation for the trait. Regarding your second point. Can you elaborate what is missing from that file? |
|
This PR is now ready for review. I think I addressed all comments and now are patiently waiting for further comments / review. |
frame/contracts/src/lib.rs
Outdated
| type TrieIdGenerator: TrieIdGenerator<Self::AccountId>; | ||
|
|
||
| /// The means of dispatching the calls to the runtime. | ||
| type Dispatcher: Dispatcher<<Self as Trait>::Call>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should we always choose a parametrized dispatcher vs just using MainDispatcher coming from system?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the discussion there.
TL;DR: I started it without thinking too much (since we doo the same with all other things) and then I found some benefits, namely using that as an escape hatch. I.e. the chain author can implement their own dispatcher that e.g. filters some calls / does additional bookkeeping and then delegates control to the system module. @athei 's idea was to actually introduce MainDispatcher to avoid hard-coding frame_system::Module directly but to just delegate to frame_system::Trait::MainDispatcher.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reasoning is to give the runtime implementer the opportunity to customize the dispatch process for some Module. With the rewrite this is much safer in a way that dispatchers cannot manually call Dispatchable::dispatch but must specify a RootDispatcher in their implementation.
b032480 to
6f1fabf
Compare
|
Resolved conflicts with master and fixed and incorporated the suggestions from the comments. Should be good for merge soon (praying to CI god). |
bin/node/runtime/src/lib.rs
Outdated
| frame_system::ChainContext<Runtime>, | ||
| Runtime, | ||
| AllModules, | ||
| (), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there use cases for customizing the top-level (transaction-level) dispatcher?
- If not, I'd remove this.
- If yes, I'd rather see an
enum DefaultDispatcher {}type that implements the dispatcher instead of(). While()works for associated types, here it lacks the context of what we are actually setting (You have to go to generic parameters and hope it has a nice readable name there).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be cases where someone want to provide another RootDispatcher. This is a far less intrusive change for a runtime implementer than modifying the frame_system which is the default RootDispatcher. Therefore I argue that it provides some benefit.
That said, I removed this parameter because we already specify the RootDispatcher through the frame_system::trait::RootDispatcher. No need for a redundant parameter.
frame/system/src/lib.rs
Outdated
| /// important to delegate to the `RootDispatcher`. There might be use cases where the | ||
| /// delegation is unwanted and the custom dispatcher dispatches the `Dispatchable` | ||
| /// itself. It is important to understand that by doing this the dispatch becomes | ||
| /// invisible to the rest of the runtime machinery which relies on the `RootDispatcher` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anytime there is something that is "important to understand" written in docs then I shivers down my spine.
This "API" (and I would use the term generously here, as it's far from codified) is dangerous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you. I assumed that we need a way to enforce the correct usage which triggered this discussion. Bottom line was that the soft API or "escape hatch" was intentional. You are rightfully unhappy about it.
gavofyork
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not at all happy about this PR. It introduces needless constraints and soft APIs that will ultimately lead to mishap.
I'd like to see a solid argument as to why needed changes for the contract-weight stuff cannot be kept isolated in the contracts module.
I agree. We must do better.
It is not only about contract-weight. As far as I understand it we want to be able to refund weight for every dispatch. For example a |
|
In response to the feedback we received I am planning the following larger changes apart from the minor issues that might or might not be resolved in the process.
|
Regardless if this approach, just emphasising that this was about more than just the contracts module and I think a weight refund in the long term is a good feature to have (through any means -- central dispatch was the best that we and @pepyakin came up with in a call. We can do better). We might not use it at the time being since in Polkadot we want to stick to a worse case scenario. But a general substrate chain might want to be picky and have some means of refunding weight. an example that we might want to consider refund even in polkadot is the phragmen submission transactions. If it goes through the hot path of replacing a solution, it takes a lot of time and it should rightfully take a whole lot of block's weight. Otherwise, it would be quite a waste to take all of that weight when we, in some cases, almost immediately reject the solution. Ideally the fee should still be taken but the weight should be refunded to the system module. |
|
TBH, I don't understand the purpose of this pr. Could someone sketch how that will help refunding weights? |
Weight refund will be done on a per dispatch basis as described here. In order to do that we decided that going through a central instance that tracks the refunded gas per dispatch and deals with nested dispatches would be the best approach. #5032 already implements this central location as a The problem there is that this approach is purely advisory. Modules have to opt in by calling this new function instead of calling |
|
Hmm, so the problem you try to solve here isn't really solved IMHO. In the default case where we don't have a wrapped call, we don't require this pr. For the other case, where we have a wrapped call this weight refund doesn't make really sense. Let me explain why. For example think about a democracy proposal. After the proposal is accepted and we reached the point it should be applied, we will dispatch it. The proposal is applied in What we really want is probably an improved |
Yes. As I already stated the implementation is lacking. Working on it right now.
No. It does not work as you describe. The caller of the nested or wrapped call gets the unspent gas as return value and is responsible for handling it. It is all written down in the notion page I linked. In order for that to work the dispatching call (here it is Then when the caller gets the unspent weight from the call it dispatched and can decide to refund it. Or it does not dispatch at all and refund all the weight that was reserved for the wrapped call.
The other PR I linked takes care of that. It changes the SignedExtensions to be aware of weight refund. It will ask the |
This introduce a `Dispatcher` trait that is used to dispatch all calls instead of directly calling `Dispatchable::dispatch`. This is needed in order to do centralistic bookeeping for all calls like for example weight refunds. Co-Authored-By: pepyakin <[email protected]>
This adds an argument to the above function wich is only constructable by the `sp_runtime::traits` module.
6f1fabf to
a6a00c3
Compare
|
It sounds like the only API change required is a minor one that just ensures dispatchables (and on_initialize) return the weight consumed.
I do not want to see any new "central instances" of anything. |
That is another approach to the weight refund task. One could modify the It comes with its own drawbacks, though.
@gavofyork Is this a direction you would rather see the weight refund stuff going? |
|
I think in the grand scheme of things, it is easier to think of it this way: Instead of asking dispatchables to return the used weight (or signal it via some function or any other means), it should be assumed that they consume all of it unless if they return some value. So, if I have understood it correctly, I agree with
|
| let ok = proposal.dispatch(frame_system::RawOrigin::Root.into()).is_ok(); | ||
| let ok = T::Dispatcher::dispatch( | ||
| proposal, | ||
| frame_system::RawOrigin::Root.into() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| frame_system::RawOrigin::Root.into() | |
| frame_system::RawOrigin::Root.into(), |
| Ok(()) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } | |
| } | |
| let r = Applyable::apply::<UnsignedValidator>(xt, dispatch_info, encoded_len)?; | ||
| let r = Applyable::apply::<UnsignedValidator, System::RootDispatcher>(xt, | ||
| dispatch_info, | ||
| encoded_len |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| encoded_len | |
| encoded_len, |
| /// For `Dispatchable` calls it is usually the responsiblity of the frame_system | ||
| /// module to function as the root `RootDispatcher`. In order to customize the | ||
| /// dispatch process for a specific module the `Dispatcher` trait can be implemented | ||
| /// and supplied to to the respective `Trait`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// and supplied to to the respective `Trait`. | |
| /// and supplied to the respective `Trait`. |
| } | ||
| } | ||
|
|
||
| /// This type describes the result of a module function call which is called through |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd put these guys next to SimpleDispatcher.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or not. There's a lot more dispatch-related and sadly the ordering is a bit confused. nvmd.
| /// In order to provide a custom dispatcher this type should be implemented and then | ||
| /// and then used by calling `dispatch` with a `Dispatchable`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// In order to provide a custom dispatcher this type should be implemented and then | |
| /// and then used by calling `dispatch` with a `Dispatchable`. | |
| /// In order to provide a custom dispatcher this type should be implemented | |
| /// and then used by calling `dispatch` with a `Dispatchable`. |
| /// Use `Self::raw_dspatch` in order to do the actual dispatch as direct call | ||
| /// of `Dispatchable::dispatch` is prevent by a token. This function should be called |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// Use `Self::raw_dspatch` in order to do the actual dispatch as direct call | |
| /// of `Dispatchable::dispatch` is prevent by a token. This function should be called | |
| /// Use `Self::raw_dspatch` in order to do the actual dispatch as direct call | |
| /// of `Dispatchable::dispatch`. ???? is prevent by a token. This function should be called |
|
Closed in favor of #5458 |
Related to #5032