Skip to content

feat(runtime): add the beefy session key and combine the v1-to-v2 and session-key migrations - #1953

Open
MicroProofs wants to merge 3 commits into
feat-aura-to-babe-migrationfrom
beefy-seat-count-as-number
Open

feat(runtime): add the beefy session key and combine the v1-to-v2 and session-key migrations#1953
MicroProofs wants to merge 3 commits into
feat-aura-to-babe-migrationfrom
beefy-seat-count-as-number

Conversation

@MicroProofs

@MicroProofs MicroProofs commented Jul 31, 2026

Copy link
Copy Markdown

Overview

Stacked on feat-aura-to-babe-migration; two commits.

Commit 1 — combine the migrations (pallet refactor, per prior review feedback: one migration must handle both the QueuedCommittee addition and a SessionKeys shape change, and the pallet storage version must never go to 3).

Replaces the pallet's pair of migrations (the queue-seeding v2::V1ToV2Migration and the FROM/TO-generic AuthorityKeysMigration) with a single combined VersionedMigration<1, 2> in migrations::v2 that translates CurrentCommittee/NextCommittee, seeds QueuedCommittee from the translated current committee, and upgrades pallet_session key storage in one gated step, ending at storage version 2.

This fixes the base branch's wiring, which ran the seed before the key translation: the seed's typed read decodes old-shaped committee bytes with the current type, fails silently, and seeds an empty QueuedCommittee — the next rotation would then hand pallet_session an empty validator set. The runtime keeps Tomasz's structure and names: AddBabeToSessionKeysMigration stays, still guard-gated on AddBabeSessionKeysMigrated (genesis-safe; the node-side committee decoders read it); only its inner migration is now the combined versioned step, and the separate seed leaves SingleBlockMigrations.

The pallet migration also hardens the placeholder contract: keys of newly added types must be pairwise distinct per validator (a shared placeholder collapses KeyOwner onto one validator; the next rotation's set_keys fails with DuplicatedKey for everyone else, and those failures are currently swallowed, #1895). The try-runtime post_upgrade round-trips KeyOwner over every key type of the new shape, and a rotation-count test pins activation timing: migrated placeholders serve the first two post-upgrade sessions; registered keys become active at the third rotation, no validator dropped.

Commit 2 — add the BEEFY session key.

opaque::SessionKeys gains beefy, translated by the same cutover migration. Placeholder = the validator's aura bytes behind the invalid SEC1 tag 0x00 (distinct per validator, collides with no real key). Candidate registrations without a BEEFY key fall back to the same derivation, mirroring the base branch's BABE-from-aura fallback. Genesis takes the configured beefy_pubkey; dev seeds derive a proper BeefyId.

All live networks (devnet, qanet, preview, preprod, mainnet) were verified at on-chain pallet storage version 1 on 2026-08-13 via state_getStorage of the storage-version key. Fresh-genesis chains start at version 2 and set the guard at genesis, so the migration no-ops there.

Also includes cargo fmt fixes for three node files on the base branch (whitespace only) so the fmt check passes.

🗹 TODO before merging

  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking) — cutover behavior preserved and fixed; enacted only by the upgrade built from the base branch
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file (changes/runtime/changed/combined-session-migration.md, changes/runtime/changed/add-beefy-session-key.md)
  • If the changes introduce a new feature, I have bumped the node minor version — rides the base branch's version handling
  • Update documentation (if relevant) — migration module docs updated
  • Updated AGENTS.md if build commands, architecture, or workflows changed — N/A
  • No new todos introduced (removes the todo: add the beefy marker)

🧪 Testing Evidence

  • pallet-session-validator-management tests: 27 passed (v1 start state, seed assertion, re-run no-op, fresh-genesis no-op, KeyOwner round-trip over added key types, rotation-count activation timing)
  • midnight-node-runtime lib tests: 18 passed (includes the base branch's committee-decode tests)
  • midnight-node lib tests: 151 passed
  • cargo check --workspace clean; --features try-runtime clean
  • cargo fmt --check clean for all files in this diff

🔱 Fork Strategy

  • Node Runtime Update — SessionKeys shape change plus the cutover migration; enacted by the upgrade built from the base branch. Metadata rebuild needed after merge.
  • Node Client Update
  • Other
  • N/A

Links

Issue: #1742

https://claude.ai/code/session_01SxL4B36LFRxeRX8HRyeSTy

@MicroProofs MicroProofs changed the title Updating midnight node to match Aiken side on a few areas Updating midnight node beefy to match Aiken side on a few areas Jul 31, 2026
Comment thread runtime/src/lib.rs Outdated
// Initializes the QueuedCommittee storage added in v2
pallet_session_validator_management::migrations::v2::V1ToV2Migration<Runtime>,
// See migrations::authority_keys when opaque::SessionKeys changes shape.
// v0->v1/v1->v2 migrations were dropped: all networks are at storage version >= 2, and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

preview, preprod, and mainnet are not at PartnerChains v2. Are they?

Unfortunately custom migration is required: one that would be able to go from V1 to V2 together with session keys migration. pallet_session_validator_management::migrations::v2::V1ToV2Migration<Runtime> was not able to do it. Careful combination of both migrations is required here.

Comment thread runtime/src/migrations.rs Outdated
grandpa: old.grandpa,
// No cross-chain id in scope here, so pallet_session key entries get a
// placeholder; they are rewritten from the committee at the next rotation.
beefy: sp_core::ecdsa::Public::from_raw([0u8; 33]).into(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would beefy recover from a period of time when node cannon find a matching private key and sign blocks?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Pushing up some changes in a moment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes as long as beefy eventually gets 2/3 vote on a stuck round then it continues until it catches up

@MicroProofs MicroProofs changed the title Updating midnight node beefy to match Aiken side on a few areas Prepare explicit BEEFY keys (Next upgrade will transition to usage) Aug 6, 2026
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch 4 times, most recently from 84e45fb to 685ea36 Compare August 7, 2026 06:19
Comment thread pallets/beefy-registration/src/lib.rs Outdated
origin: OriginFor<T>,
updates: BoundedVec<(T::AuthorityId, Option<BeefyKeyBytes>), T::MaxUpdatesPerBlock>,
) -> DispatchResult {
ensure_none(origin)?;

@LGLO LGLO Aug 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've seen recommendations to have a storage (killed in in on_finalize) to prevent malicious nodes include multiple calls of None origin and fill up a block.
If this pallet will be removed before opening mainnet for registered candidates, I think it is okay to not add this complexity.

EDIT: I see we are looking for registered candidates keys as well, so this perhaps will stay and should be protected from multiple inclusion.

Comment thread changes/node/added/beefy-key-tooling.md Outdated
The `start-node` wizard now warns about a missing `beef` key instead of refusing
to start. The node also logs a warning when it starts as an authority without
this key because it cannot participate in BEEFY voting. BEEFY keys are never
derived from the cross chain key, and the `AuraToBabeMigrationKeystore`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Local-environment setup (res/local/permissioned-candidates-config.json and keystores in local-environment itself) will require update.

@datadog-official

This comment has been minimized.

@MicroProofs MicroProofs changed the title Prepare explicit BEEFY keys (Next upgrade will transition to usage) refactor(runtime): combine committee v1-to-v2 and session-key migrations Aug 19, 2026
@MicroProofs MicroProofs added the bot:ai-assisted Authored or substantially edited by an AI agent label Aug 19, 2026
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch 4 times, most recently from 14bc529 to 5fd5369 Compare August 23, 2026 18:00
@MicroProofs
MicroProofs requested a review from LGLO August 23, 2026 19:43
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch from 5fd5369 to 90662e9 Compare August 23, 2026 19:49
@MicroProofs
MicroProofs marked this pull request as ready for review August 28, 2026 04:08
@MicroProofs
MicroProofs requested a review from a team as a code owner August 28, 2026 04:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90662e949d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread runtime/src/lib.rs Outdated
Comment on lines +376 to +377
// Wire the cutover migration from `crate::migrations::authority_keys` here.
type SingleBlockMigrations = ();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the v1-to-v2 migration wired until cutover

For any existing chain whose on-chain pallet storage version is still 1, installing this runtime now leaves QueuedCommittee absent because no migration runs. The queue-based code then reads its ValueQuery default (epoch 0 and an empty committee), uses that to select epoch 1 in create_inherent, and can promote the empty value during rotation. Deferring the combined migration until a later session-key upgrade is only safe if this intermediate runtime can never be enacted; otherwise the seed-only migration must remain wired or the combined migration must run in this upgrade.

Useful? React with 👍 / 👎.

@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch from 90662e9 to 0a8e027 Compare August 28, 2026 07:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a8e027923

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +313 to +315
.map(|member| {
let member = <OldCommitteeMember as UpgradeCommitteeMember<T>>::upgrade(member);
(member.authority_id(), member.authority_keys())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve old authority IDs in the migration fingerprint

When a runtime's UpgradeCommitteeMember implementation accidentally changes an authority ID or other non-key member data, this fingerprint applies the same faulty upgrade to the saved pre-upgrade value that the migration applied to storage. The two fingerprints therefore still match and try-runtime reports that committee membership was preserved even though it was corrupted. Construct the expected fingerprint from the old member's original authority_id() and convert only its authority keys, as the previous implementation did.

Useful? React with 👍 / 👎.

Comment on lines +1 to +2
#runtime #migration #committee-selection
# Combine committee v1-to-v2 and session-key migrations

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add a toolkit change entry for the public API removal

This commit removes the public reusable AuthorityKeysMigration API and the migrations::v2 module from the Partner Chains toolkit, but records the change only under changes/runtime; partner-chains/changelog.md still advertises AuthorityKeysMigration as an unreleased reusable API. Toolkit consumers therefore receive contradictory release notes and no component change entry warning them that their imports and migration wiring must change. Add the corresponding toolkit change file and update the unreleased Partner Chains changelog.

AGENTS.md reference: AGENTS.md:L197-L201

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex is stubborn and thinks #toolkit marker is for PC toolkit, but it is not.

@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch 3 times, most recently from 7f99140 to 087b41f Compare August 29, 2026 16:30
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T17:57:52.012239Z b5ba330 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch 2 times, most recently from b9d8fe4 to 5b2319f Compare August 31, 2026 22:48
@MicroProofs MicroProofs changed the title refactor(runtime): combine committee v1-to-v2 and session-key migrations refactor(runtime): combine committee v1-to-v2 and session-key migrations (babe cutover) Aug 31, 2026
@MicroProofs
MicroProofs changed the base branch from main to feat-aura-to-babe-migration August 31, 2026 22:51
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

.map(|h| resolver.engine_at_parent(*h.parent_hash()))
.unwrap_or(ActiveEngine::Aura);

P1 Badge Avoid resolving engines from unimported batch parents

When sync submits a contiguous multi-block batch, only the first block's parent is normally present in the client state; the parents of later blocks are earlier entries in the same batch. These runtime API queries therefore fail and default those later blocks to AURA, so post-flip BABE blocks are sent through the AURA verifier and rejected. Dispatch must classify blocks without requiring their in-batch parents to have already been imported, or process the batch sequentially across the engine boundary.


let mut notifications = client.import_notification_stream();

P2 Badge Subscribe before checking whether the flip occurred

If the flip block becomes best after the initial best_hash check but before this subscription is created, its notification is lost. On a validator that then has no peer producing a later BABE block—most clearly a single-validator network—the supervisor keeps polling AURA forever even though the best state is already BABE, so authoring halts at the transition. Create the notification stream before the initial state check so the check and subsequent wait cannot miss the one-time flip.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@MicroProofs MicroProofs changed the title refactor(runtime): combine committee v1-to-v2 and session-key migrations (babe cutover) feat(runtime): add the beefy session key and combine the v1-to-v2 and session-key migrations Aug 31, 2026
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch from 693a1ba to 241f19c Compare September 1, 2026 00:49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 241f19c8cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread runtime/src/migrations.rs
Comment on lines +51 to +56
let mut beefy_raw = [0u8; 33];
beefy_raw[1..].copy_from_slice(&aura_raw);
SessionKeys {
babe: sp_core::sr25519::Public::from_raw(aura_raw).into(),
// Invalid SEC1 tag keeps the placeholder distinct from any real key.
beefy: sp_core::ecdsa::Public::from_raw(beefy_raw).into(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep migrated BEEFY authorities signable

On a v1 chain, this migration installs a BEEFY public key with the invalid SEC1 tag 0x00 for every validator. At the first session rotation those migrated QueuedKeys become the pallet_beefy authority set, but no private key can correspond to these values, so BEEFY cannot reach a signing quorum; even registrations already updated with real BEEFY keys do not become active until subsequent rotations, while registrations without BEEFY retain the same invalid fallback indefinitely. Preserve a signable legacy key (the existing cross-chain/BEEFY identity) or delay enabling the BEEFY session handler until real keys are active.

AGENTS.md reference: AGENTS.md:L69-L69

Useful? React with 👍 / 👎.

Comment thread runtime/src/lib.rs
Comment on lines 222 to +225
pub aura: Aura,
pub grandpa: Grandpa,
pub babe: Babe,
// todo: add the beefy
// pub beefy: Beefy,
pub beefy: Beefy,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Rebuild metadata after changing SessionKeys

Adding this field changes the SCALE layout exposed through committee storage and the session-validator-management runtime API, but this commit leaves every file under metadata/static/ untouched. Earthfile:1101-1102 explicitly packages and compares midnight_metadata.scale, so downstream tooling using the checked-in artifact continues to see the schema without beefy and can misdecode four-key committee values; regenerate the repository metadata with this runtime change.

AGENTS.md reference: AGENTS.md:L146-L149

Useful? React with 👍 / 👎.

Comment thread runtime/src/migrations.rs
db.reads(1)
} else {
let weight = <Inner as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade();
let weight = <Inner as OnRuntimeUpgrade>::on_runtime_upgrade();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Forward try-runtime hooks to the combined migration

When try-runtime executes the configured AddBabeToSessionKeysMigration, this direct on_runtime_upgrade call runs only the inner migration body; the outer type has no pre_upgrade forwarding and its post_upgrade checks only the guard. Consequently all validation in v2.rs—including committee preservation, queue seeding, session-key conversion, and storage-version checks—is skipped for the actual Midnight runtime, so a corrupted migration can pass try-runtime as long as the guard is written. Delegate the inner pre/post state through this wrapper or wire the versioned migration directly.

Useful? React with 👍 / 👎.

Signed-off-by: microproofs <kwhitemsg@gmail.com>
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch from 241f19c to 58f2ff7 Compare September 1, 2026 02:28
@MicroProofs

Copy link
Copy Markdown
Author

/bot rebuild-metadata

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58f2ff7c6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

grandpa: keys.grandpa_pubkey.into(),
// Fall back to the AURA key when no BABE key is configured (both sr25519).
babe: keys.babe_pubkey.unwrap_or(keys.aura_pubkey).into(),
beefy: keys.beefy_pubkey.into(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add a node change entry for BEEFY chain-spec support

When the node release notes are assembled, the new chain-spec behavior and the node crate's 2.1.1 version bump will have no node-component entry because this commit adds only runtime and toolkit change files. Add a corresponding changes/node/added/ or changes/node/changed/ entry so node consumers are notified that generated genesis session keys now include BEEFY.

AGENTS.md reference: AGENTS.md:L197-L201

Useful? React with 👍 / 👎.

Signed-off-by: microproofs <kwhitemsg@gmail.com>
…rsion

Signed-off-by: microproofs <kwhitemsg@gmail.com>
@MicroProofs
MicroProofs force-pushed the beefy-seat-count-as-number branch from 58f2ff7 to b5ba330 Compare September 1, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ai-assisted Authored or substantially edited by an AI agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants