Skip to content

[author-inherent] Move kick_off_authorship_validation() to post_inherents()#93

Open
arturgontijo wants to merge 10 commits intomainfrom
artur/post-inherents
Open

[author-inherent] Move kick_off_authorship_validation() to post_inherents()#93
arturgontijo wants to merge 10 commits intomainfrom
artur/post-inherents

Conversation

@arturgontijo
Copy link
Copy Markdown
Contributor

@arturgontijo arturgontijo commented Feb 9, 2026

This PR moves the kick_off_authorship_validation() logic to the post_inherents().

It also adds integrity_test() hook with some checks.

Note: Do we need more integrity checks there? I decided to keep tests.rs simple as this new approach is.

As we see here, integrity_tests is also being tested too.

➜  moonkit git:(artur/post-inherents) cargo test -p pallet-author-inherent                                                        
warning: profile package spec `cranelift-wasm` in profile `dev` did not match any packages
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1.27s
     Running unittests src/lib.rs (target/debug/deps/pallet_author_inherent-95dea1c55ff74551)

running 3 tests
test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok
test mock::test_genesis_config_builds ... ok
test tests::test_author_is_extracted_and_stored_from_pre_runtime_digest ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

⚠️ Breaking changes

Downstream runtimes using pallet-author-inherent must update their integration:

  • Add AuthorInherent to frame_system::Config::PostInherents.
  • Remove Call / Inherent from the pallet’s construct_runtime! entry:
    • before: pallet_author_inherent::{Pallet, Call, Storage, Inherent}
    • after: pallet_author_inherent::{Pallet, Storage}
  • Remove type WeightInfo = ... from impl pallet_author_inherent::Config.

@arturgontijo arturgontijo self-assigned this Feb 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces inherent-driven author validation with a PostInherents hook that extracts authors from pre-runtime digests and validates via CanAuthor; removes WeightInfo, benchmark and autogenerated weight files; updates mocks, tests, Cargo manifest, and runtime wiring to match the new PostInherents flow.

Changes

Cohort / File(s) Summary
Dependency manifest
pallets/author-inherent/Cargo.toml
Replaced sp-api/sp-inherents usage with sp-core in dependencies and features.std; removed sp-core from dev-dependencies.
Pallet implementation
pallets/author-inherent/src/lib.rs
Removed inherent-based lifecycle (InherentIncluded, ProvideInherent, kick_off_authorship_validation, InherentError, WeightInfo) and moved author extraction/eligibility to impl PostInherents that reads pre-runtime digests and writes Author<T>. Simplified on_initialize to clear Author<T>.
Benchmarks & weights
pallets/author-inherent/src/benchmarks.rs, pallets/author-inherent/src/weights.rs
Deleted runtime benchmark file and autogenerated weights file (removed WeightInfo, SubstrateWeight, and unit impls).
Mocks
pallets/author-inherent/src/mock.rs
Updated construct_runtime! entry to drop Call for AuthorInherent; set frame_system::Config::PostInherents = AuthorInherent; added BOB constants and TestCanAuthor implementing nimbus_primitives::CanAuthor; removed WeightInfo from test config.
Tests
pallets/author-inherent/src/tests.rs
Rewrote tests to call post_inherents() instead of on_initialize/on_finalize flows; added panic-path tests (missing author, unmapped NimbusId, invalid digest, ineligible author) and lifecycle test verifying on_initialize clears Author then post_inherents repopulates it.
Runtime integration
template/runtime/src/lib.rs
Added AuthorInherent to frame_system::Config::PostInherents; removed WeightInfo assoc type from pallet_author_inherent::Config; changed construct_runtime! export to only Pallet and Storage for AuthorInherent.
Docs / templates
template/README.md
Updated local dev instructions to install Zombienet via npm install -g @zombienet/cli``.

Sequence Diagram

sequenceDiagram
    participant System as Frame System
    participant PostInherents as PostInherents Hook
    participant Pallet as AuthorInherent Pallet
    participant CanAuthor as CanAuthor Validator
    participant Storage as Runtime Storage

    System->>PostInherents: post_inherents()
    PostInherents->>Pallet: read pre-runtime digest
    Pallet->>Pallet: extract NimbusId, author, slot
    Pallet->>CanAuthor: can_author(author, slot)?
    alt author valid
        CanAuthor-->>Pallet: true
        Pallet->>Storage: write `Author<T>`
        Storage-->>Pallet: ack
    else missing/invalid/ineligible
        CanAuthor-->>Pallet: false / missing
        Pallet-->>PostInherents: panic / abort
    end
    PostInherents-->>System: complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main change: moving kick_off_authorship_validation() logic to the post_inherents() hook, which is the primary objective of this changeset.
Description check ✅ Passed The PR description is directly related to the changeset, explaining the move from inherent-based to post_inherents-hook mechanism, mentioning integrity tests, and documenting breaking changes for downstream runtimes.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch artur/post-inherents

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pallets/author-inherent/src/tests.rs (1)

52-61: Add one test for the “author not eligible” panic path.

Current coverage validates success + missing digest, but not the eligibility assert in pallets/author-inherent/src/lib.rs Line 168.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallets/author-inherent/src/tests.rs` around lines 52 - 61, Add a test in the
tests module that triggers the "author not eligible" assert path in the pallet:
use new_test_ext() and System::initialize with a Digest containing a pre-runtime
author digest (so the missing-digest case is not hit), ensure the test
environment does not mark that author as eligible (so the eligibility assert in
pallets/author-inherent/src/lib.rs is hit), call
AuthorInherent::post_inherents() and annotate the test with
#[should_panic(expected = "Author not eligible")] so the panic message matches
the pallet's assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pallets/author-inherent/src/lib.rs`:
- Around line 89-106: Implement an on_initialize hook in the existing Hooks impl
for Pallet<T> that clears the Author storage at the start of each block (e.g.,
call Author::<T>::kill() or set it to None) so the Get<T::AuthorId> trait
implementation and any pallet reading Pallet::<T>::author() do not observe stale
author data before post_inherents() runs; add this logic to the
Hooks<BlockNumberFor<T>> for Pallet<T> alongside the existing integrity_test and
ensure it only clears Author (leave post_inherents() assignment unchanged).

---

Nitpick comments:
In `@pallets/author-inherent/src/tests.rs`:
- Around line 52-61: Add a test in the tests module that triggers the "author
not eligible" assert path in the pallet: use new_test_ext() and
System::initialize with a Digest containing a pre-runtime author digest (so the
missing-digest case is not hit), ensure the test environment does not mark that
author as eligible (so the eligibility assert in
pallets/author-inherent/src/lib.rs is hit), call
AuthorInherent::post_inherents() and annotate the test with
#[should_panic(expected = "Author not eligible")] so the panic message matches
the pallet's assertion.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5234bc8 and 2a7926e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • pallets/author-inherent/Cargo.toml
  • pallets/author-inherent/src/benchmarks.rs
  • pallets/author-inherent/src/lib.rs
  • pallets/author-inherent/src/mock.rs
  • pallets/author-inherent/src/tests.rs
  • pallets/author-inherent/src/weights.rs
  • template/runtime/src/lib.rs
💤 Files with no reviewable changes (2)
  • pallets/author-inherent/src/weights.rs
  • pallets/author-inherent/src/benchmarks.rs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
pallets/author-inherent/src/tests.rs (1)

52-115: Add a test for the “author not eligible” panic path.

Current tests validate “missing digest author” and lifecycle behavior, but do not exercise the assertion branch where the digest author exists and T::CanAuthor::can_author(...) returns false (lib.rs Lines 176-179). Adding this would close the main negative-path gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallets/author-inherent/src/tests.rs` around lines 52 - 115, Add a new unit
test (e.g., test_post_inherents_panics_when_author_not_eligible) that
initializes System with a pre-runtime digest containing ALICE's NimbusId and
configures the test runtime so T::CanAuthor::can_author(...) returns false for
that author, then call AuthorInherent::post_inherents() wrapped with
#[should_panic(expected = "Block invalid, author not eligible")] to assert the
panic; reference AuthorInherent::post_inherents and the T::CanAuthor::can_author
check to locate the assertion branch to exercise.
pallets/author-inherent/src/lib.rs (1)

99-106: integrity_test() is coupled to mutable storage state.

At Line 104, asserting Author::<T>::get().is_none() makes integrity checks depend on current storage assumptions rather than invariant/type-level constraints. This can become brittle if runtime initialization/migrations evolve.

♻️ Proposed adjustment
 		fn integrity_test() {
 			// Test that SlotBeacon can be called and returns a valid slot
 			let slot = T::SlotBeacon::slot();
-			// Author storage should be accessible (this is a compile-time check)
-			assert!(
-				Author::<T>::get().is_none(),
-				"Author storage should be none"
-			);
 
 			// Test that CanAuthor trait can be called
 			let _ = Pallet::<T>::can_author(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallets/author-inherent/src/lib.rs` around lines 99 - 106, The integrity_test
currently depends on runtime storage by asserting Author::<T>::get().is_none();
remove that state-dependent assertion and replace it with a compile-time
type-level check so the test stays invariant: keep the SlotBeacon::slot() call,
then add a typed binding like let _ : Option<T::AccountId> = Author::<T>::get();
(or simply remove the Author assertion entirely) so you verify the Author
storage accessor compiles without depending on current storage contents; update
the integrity_test function (reference: integrity_test, T::SlotBeacon::slot,
Author::<T>::get) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pallets/author-inherent/src/lib.rs`:
- Around line 173-174: The comment claiming "First check that the slot number is
valid (greater than the previous highest)" is inaccurate: the code only reads
the current slot into new_slot via T::SlotBeacon::slot() and does not perform a
monotonicity check. Update the comment to state that this line simply retrieves
the current slot from the SlotBeacon (reference new_slot and
T::SlotBeacon::slot()), or if you intended to enforce monotonicity, implement a
comparison against the stored highest slot (e.g., a Pallet storage item like
HighestSlot) and handle rejections accordingly; but do not leave the existing
monotonicity wording without the corresponding check.

---

Nitpick comments:
In `@pallets/author-inherent/src/lib.rs`:
- Around line 99-106: The integrity_test currently depends on runtime storage by
asserting Author::<T>::get().is_none(); remove that state-dependent assertion
and replace it with a compile-time type-level check so the test stays invariant:
keep the SlotBeacon::slot() call, then add a typed binding like let _ :
Option<T::AccountId> = Author::<T>::get(); (or simply remove the Author
assertion entirely) so you verify the Author storage accessor compiles without
depending on current storage contents; update the integrity_test function
(reference: integrity_test, T::SlotBeacon::slot, Author::<T>::get) accordingly.

In `@pallets/author-inherent/src/tests.rs`:
- Around line 52-115: Add a new unit test (e.g.,
test_post_inherents_panics_when_author_not_eligible) that initializes System
with a pre-runtime digest containing ALICE's NimbusId and configures the test
runtime so T::CanAuthor::can_author(...) returns false for that author, then
call AuthorInherent::post_inherents() wrapped with #[should_panic(expected =
"Block invalid, author not eligible")] to assert the panic; reference
AuthorInherent::post_inherents and the T::CanAuthor::can_author check to locate
the assertion branch to exercise.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7926e and f9b6b08.

📒 Files selected for processing (2)
  • pallets/author-inherent/src/lib.rs
  • pallets/author-inherent/src/tests.rs

@manuelmauro manuelmauro self-requested a review March 26, 2026 16:35
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
pallets/author-inherent/src/lib.rs (1)

90-95: ⚠️ Potential issue | 🟠 Major

post_inherents() work is still unmetered.

on_initialize() only charges the Author::<T>::kill(), but the new path also scans digests, decodes NimbusId, does AccountLookup/CanAuthor, and writes Author::<T> again. Because post_inherents() cannot return weight, every block is now undercharged unless that worst-case cost is moved back onto a weighted path again, e.g. via a dedicated WeightInfo entry charged from on_initialize().

In Substrate FRAME, does `frame_support::traits::PostInherents::post_inherents()` participate in block weight accounting, or should its worst-case work be charged from `on_initialize()` or another weighted path?

Also applies to: 150-162

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallets/author-inherent/src/lib.rs` around lines 90 - 95, The
post_inherents() path does non-trivial work (scanning digests, decoding
NimbusId, performing AccountLookup/CanAuthor, and writing Author::<T>) but its
cost is not charged; update on_initialize() to include a worst-case weight for
that work by adding a WeightInfo entry (e.g. WeightInfo::post_inherents or
similar) and charge it via T::DbWeight::get().writes(...) +
WeightInfo::post_inherents() (or equivalent) in the on_initialize()
implementation so the expensive operations performed in post_inherents() are
accounted for; reference on_initialize, post_inherents, Author::<T>::kill, and
the WeightInfo trait when implementing this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pallets/author-inherent/src/lib.rs`:
- Around line 90-95: The post_inherents() path does non-trivial work (scanning
digests, decoding NimbusId, performing AccountLookup/CanAuthor, and writing
Author::<T>) but its cost is not charged; update on_initialize() to include a
worst-case weight for that work by adding a WeightInfo entry (e.g.
WeightInfo::post_inherents or similar) and charge it via
T::DbWeight::get().writes(...) + WeightInfo::post_inherents() (or equivalent) in
the on_initialize() implementation so the expensive operations performed in
post_inherents() are accounted for; reference on_initialize, post_inherents,
Author::<T>::kill, and the WeightInfo trait when implementing this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ad6bb59-a8d5-4249-baae-bad77bffae4b

📥 Commits

Reviewing files that changed from the base of the PR and between 836ce30 and afe01bb.

📒 Files selected for processing (3)
  • pallets/author-inherent/src/lib.rs
  • pallets/author-inherent/src/mock.rs
  • pallets/author-inherent/src/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • pallets/author-inherent/src/mock.rs

Copy link
Copy Markdown
Collaborator

@librelois librelois left a comment

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants