Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add after_inherents #14414

Draft
wants to merge 47 commits into
base: master
Choose a base branch
from
Draft

Add after_inherents #14414

wants to merge 47 commits into from

Conversation

ggwpez
Copy link
Member

@ggwpez ggwpez commented Jun 19, 2023

Adds an after_inherents function to the BlockBuilder runtime API. The function is not exposed as hook to the pallets. (Preparation for the MBMs, as requested here)

after_inherents is invoked after Inherents and before any Transaction processing.
It returns RuntimeExecutiveMode which decides whether Transactions can be included in this block. The BlockBuilder respects this return value and includes only inherents otherwise.
frame-executive refuses to import a block when after_inherents returns TransactionsForbidden but transactions are present. This is done by changing ensure_inherents_are_first to return the number of inherents and comparing that with the number of extrinsics in the block.

Changes:

  • 🚨 Add after_inherents to Runtime API BlockBuilder.
  • 🚨 Change initialize_block from Runtime API Core to return RuntimeExecutiveMode.
  • Add variant TransactionsForbidden to EndProposingReason.
  • Move frame-executive tests to own file.
  • Let ensure_inherents_are_first return the number of inherents.
  • Renamed execute_extrinsics_with_book_keeping to apply_extrinsic and add a apply_extrinsic_with_mode variant.

Integration

Add it to your runtime implementation of Core and BlockBuilder:

diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
@@ impl_runtime_apis! {
	impl sp_block_builder::BlockBuilder<Block> for Runtime {
 		...
+
+		fn after_inherents() {
+			Executive::after_inherents()
+		}

		...
	}
	impl sp_block_builder::Core<Block> for Runtime {
-		fn initialize_block(header: &<Block as BlockT>::Header) {
+		fn initialize_block(header: &<Block as BlockT>::Header) -> RuntimeExecutiveMode {
			Executive::initialize_block(header)
		}

		...
	}

TODO

  • Check that try-runtime still works
  • Ensure backwards compatibility with old Runtime APIs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
@ggwpez ggwpez self-assigned this Jun 19, 2023
@ggwpez ggwpez added A3-in_progress Pull request is in progress. No review needed at this stage. C1-low PR touches the given topic and has a low impact on builders. D5-nicetohaveaudit ⚠️ PR contains trivial changes to logic that should be properly reviewed. B1-note_worthy Changes should be noted in the release notes T1-runtime This PR/Issue is related to the topic “runtime”. labels Jun 19, 2023
ggwpez added 11 commits June 19, 2023 13:36
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
@ggwpez ggwpez added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Jun 20, 2023
@ggwpez ggwpez marked this pull request as ready for review June 20, 2023 10:26
@ggwpez ggwpez requested review from a team June 20, 2023 10:26
@liamaharon liamaharon self-requested a review June 20, 2023 12:00
@kianenigma
Copy link
Contributor

kianenigma commented Jun 20, 2023

The only thing that was not possible to address is the inclusion of Mandatory extrinsics when after_inherents forbids extrinsics. The Block builder is unaware of the concept of a DispatchClass, so it cannot make distinctions by it. Any idea how to fix it in a not-too-ugly way? Or are we fine with the current state?

From what I can say, we must be fine with it as there is no other way, unless if we want to leak the concept of DispatchClass or something similar to the client side.

I don't think it is particularly horrible to do that. Not suggesting we leak DispatchClass as-is, but we can consider introducing a new primitive that would be like a u8 indicating the significance of each extrinsic to the block builder (something similar to this). after_inherents can then return something like 128 which indicates that any extrinsic who's significance is higher than 128 should be permitted.

Or even more broadly, the runtime would return a significance: u8 in response to initialize_block, and the block builder would know that it should only add extrinsics of such significance or more into the block.

The client would indeed need to know the significance: u8 of each extrinsic, which can be part of the validate_transaction call, or yet a new API. The runtime should preferably make sure significance is more or less static, in order to not fool the block builder.

This can also help relax the strict need that Inherents must always be first (can't find the discussion, but was talked about recently -- cc @thiolliere). They can appear at any point in the block, and as long as their significance is higher than what initialize_block has dictated, it is acceptable.

All of this can also be backwards compatible, as by returning 255 or 0 you can block all or nothing, which is basically the current behavior.


Truthfully, I don't think you should block this PR or MBM for what I am saying, but I hope to give you ideas to refactor the API to something a bit clearer in the long run. Right now, it seems like we are just adding APIs that fix the problem right in front of us and nothing more.

@ggwpez
Copy link
Member Author

ggwpez commented Jun 20, 2023

I don't think it is particularly horrible to do that. Not suggesting we leak DispatchClass as-is, but we can consider introducing a new primitive that would be like a u8 indicating the significance of each extrinsic to the block builder (something similar to this). after_inherents can then return something like 128 which indicates that any extrinsic who's significance is higher than 128 should be permitted.

Okay now i understood how that should work, because i did not from the other comment alone, thanks 😆

Nothing to add so far. Hopefully it is possible in validate_transaction to avoid another runtime API.

@bkchr
Copy link
Member

bkchr commented Jun 20, 2023

In general I don't see the need why after_inherents needs to return anything at all? The block builder is just dumb and tries to push transactions until the runtime shouts "stop", not sure why we need to change this here? With MBM we would set the block weight to 100% after we have executed them in a block and then we would get this "stop adding new extrinsics" for free?

This can also help relax the strict need that Inherents must always be first (can't find the discussion, but was talked about recently -- cc @thiolliere). They can appear at any point in the block, and as long as their significance is higher than what initialize_block has dictated, it is acceptable.

Not sure why you want this? Finally we start coming up with a proper way of ensuring that certain operations are not executed before inherents are applied and then you want to revert this again?

Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

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

Yeah, I don't really see a need to let after_inherents return some value. We should just handle this by "artificially" using all weight of the block. Then there is no need to add all these extra checks that you have introduced.

bin/node/runtime/src/lib.rs Outdated Show resolved Hide resolved
client/block-builder/src/lib.rs Outdated Show resolved Hide resolved
@paritytech-ci paritytech-ci requested a review from a team June 20, 2023 20:55
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
@ggwpez ggwpez added A3-in_progress Pull request is in progress. No review needed at this stage. and removed A0-please_review Pull request needs code review. labels Jul 11, 2023
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Copy link
Contributor

@muharem muharem left a comment

Choose a reason for hiding this comment

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

looks correct to me.
without approve solely to draw in engineers who are more experienced in this topic.

frame/executive/src/lib.rs Outdated Show resolved Hide resolved
frame/executive/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines 952 to 970
/// All logic is allowed to run.
///
/// For example:
/// - Extrinsics with dispatch class `Normal`.
/// - `on_idle` hook.
Normal,
/// Only _necessary_ logic is allowed to run.
///
/// Explicitly forbidden are:
/// - Extrinsics with dispatch classes `Normal` and `Operational`.
/// - `on_idle` and `poll` hooks.
///
/// Explicitly allowed are:
/// - Mandatory extrinsics (i.e. via OCW).
/// - `on_initialize` and `on_finalize` hooks.
/// - Storage migrations.
///
/// Everything in between is to be judged by the runtime.
Minimal,
Copy link
Contributor

Choose a reason for hiding this comment

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

I do like Normal and Minimal (or could be System). it's not only about non-inherents inclusion, but also about hooks and could be more in the future. More generic names with documentation looks like more flexible approach here. My five cents.

@ggwpez ggwpez added D9-needsaudit 👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited and removed D5-nicetohaveaudit ⚠️ PR contains trivial changes to logic that should be properly reviewed. labels Jul 20, 2023
}

/// Same as `apply_extrinsic` but gets the `mode` directly passed in.
pub fn apply_extrinsic_with_mode(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub fn apply_extrinsic_with_mode(
fn apply_extrinsic_with_mode(

might help the readability of this file if we only make the things used in the runtime public, and even name them such that it is clear in which API they are meant to be used eg pub fn block_builder_finalze_block()

Copy link
Member Author

Choose a reason for hiding this comment

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

I will make them private otherwise, yes. Not sure if we should do the renaming though. WDYT @bkchr?

return Err(InvalidTransaction::BadMandatory.into())
}
if mode == ExtrinsicInclusionMode::OnlyInherents && !mandatory {
// Note: The block builder should never try to do this.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why? imagine we are running MBMs, and there is some non-mandatory inherent (of which we currently don't have an example, but it could exist).

It seems like ExtrinsicInclusionMode::OnlyInherents is actually ExtrinsicInclusionMode::OnlyMandatoryInherents? 🙈

Copy link
Member Author

Choose a reason for hiding this comment

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

There could still be optional inherents, the blockbuilder would just omit them.

Mandatory has two meanings that can get mixed up here: Mandatory in the sense of being infallible and Mandatory in the sense that it needs to execute in every block. We can still have optional inherents that are non-mandatory in the second sense by letting the blockbuilder omit them.
But having an inherents that can fail is not quite useful i think. LMK if you meant something else 😄

Not sure about the OnlyMandatoryInherents, maybe but since there are no non-mandatory it is not adding any information, or?

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

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

Code is solid, but I think the namings we are using here are prone to becoming a puzzle for our future selves, or even worse, others to understand this code.

If no better option exists, I suggest putting some extra documentation about the known behavior of this entire system once the MBM system is final.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
@JoshOrndorff
Copy link
Contributor

If you are breaking the core and blockbuilder apis anyway, maybe this is a good time to move initialize_block to its more natural place in the block builder api.

See https://substrate.stackexchange.com/questions/3128/why-is-initialize-block-in-the-core-runtime-api-as-opposed-to-blockbuilder for details.

@ggwpez
Copy link
Member Author

ggwpez commented Jul 26, 2023

If you are breaking the core and blockbuilder apis anyway, maybe this is a good time to move initialize_block to its more natural place in the block builder api.

See https://substrate.stackexchange.com/questions/3128/why-is-initialize-block-in-the-core-runtime-api-as-opposed-to-blockbuilder for details.

Yes i actually did that in the MR, but then reverted it since we cannot keep backwards compatibility by completely removing a runtime API function. Normally we have this changed_in attribute to indicate change and keep the old version accessible, but there is no removed_in... and even if there were one: then we would have the function in the runtime API twice (which is also ugly).

@ggwpez ggwpez marked this pull request as draft July 26, 2023 18:43
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
@paritytech-cicd-pr
Copy link

The CI pipeline was cancelled due to failure one of the required jobs.
Job name: test-linux-stable
Logs: https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/3339614

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A3-in_progress Pull request is in progress. No review needed at this stage. B1-note_worthy Changes should be noted in the release notes C1-low PR touches the given topic and has a low impact on builders. D9-needsaudit 👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited T1-runtime This PR/Issue is related to the topic “runtime”.
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

10 participants