Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dc9400a
session/staking/westend AH and RC: allow to set/purge session keys vi…
sigurpol Dec 15, 2025
0955884
ah-client: proper weight for set_keys_from_ah, purge_keys_from_ah
sigurpol Dec 17, 2025
3e86054
staking-async/rc-client: improve documentation
sigurpol Dec 17, 2025
1c09588
staking-async-rc-client: only validators are allowed to set/purge keys
sigurpol Dec 18, 2025
9b76270
fix prdoc
sigurpol Dec 18, 2025
4dd074b
staking-async-rc-client: session keys as raw bytes
sigurpol Dec 18, 2025
45a7526
staking-async-rc-client: improve documentation
sigurpol Dec 18, 2025
d36a42c
improve prdoc
sigurpol Dec 18, 2025
5064a6c
staking-async/ah: add unit testss
sigurpol Dec 18, 2025
619ec8a
staking-async/rc: add unit tests
sigurpol Dec 18, 2025
668f7a3
staking-async/ah: add e2e test
sigurpol Dec 18, 2025
e5b2ef8
staking-async/ah: more tests
sigurpol Dec 18, 2025
fe233b0
session keys and ownership proof fully validated on AH
sigurpol Dec 19, 2025
5fd5a0c
Merge branch 'master' into sigurpol-session-keys-asset-hub
sigurpol Dec 19, 2025
d4c3f2f
Updated ownership_proof_is_valid call to use the new 2 arg signature
sigurpol Dec 19, 2025
96a5e00
more tests
sigurpol Dec 19, 2025
bcd7664
Merge branch 'master' into sigurpol-session-keys-asset-hub
sigurpol Dec 24, 2025
e4af53f
staking-async: fix CI
sigurpol Dec 24, 2025
0a13bb5
asset-hub-westend: add support for staking proxy
sigurpol Dec 24, 2025
294704e
staking-async: tests around staking proxy
sigurpol Dec 24, 2025
b28ced4
asset-hub-westend: test keys match between AH and RC
sigurpol Dec 24, 2025
a498cbb
staking-async/rc: add benchmarks for set/purge_keys
sigurpol Dec 26, 2025
3519aef
Merge branch 'master' into sigurpol-session-keys-asset-hub
sigurpol Dec 26, 2025
fa76173
staking-async/rc: bounded vec for set_keys
sigurpol Dec 26, 2025
be0cd04
Update from github-actions[bot] running command 'bench --pallet palle…
github-actions[bot] Dec 26, 2025
23f49e5
Cleanup
sigurpol Dec 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ sp-storage = { workspace = true }
sp-transaction-pool = { workspace = true }
sp-version = { workspace = true }

# Consensus primitives for Relay Chain SessionKeys validation
sp-authority-discovery = { workspace = true }
sp-consensus-babe = { workspace = true }
sp-consensus-beefy = { workspace = true }
sp-consensus-grandpa = { workspace = true }

# num-traits feature needed for dex integer sq root:
primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true }

Expand All @@ -102,6 +108,7 @@ pallet-xcm = { workspace = true }
pallet-xcm-benchmarks = { optional = true, workspace = true }
pallet-xcm-precompiles = { workspace = true }
polkadot-parachain-primitives = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
westend-runtime-constants = { workspace = true }
xcm = { workspace = true }
Expand Down Expand Up @@ -142,6 +149,7 @@ asset-test-utils = { workspace = true, default-features = true }
pallet-revive-fixtures = { workspace = true, default-features = true }
parachains-runtimes-test-utils = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
westend-runtime = { workspace = true, default-features = true }

[build-dependencies]
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }
Expand Down Expand Up @@ -209,12 +217,14 @@ runtime-benchmarks = [
"pallet-xcm/runtime-benchmarks",
"parachains-common/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-primitives/runtime-benchmarks",
"polkadot-runtime-common/runtime-benchmarks",
"snowbridge-pallet-system-frontend/runtime-benchmarks",
"snowbridge-runtime-common/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"westend-runtime-constants/runtime-benchmarks",
"westend-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"xcm-runtime-apis/runtime-benchmarks",
Expand Down Expand Up @@ -286,6 +296,7 @@ try-runtime = [
"snowbridge-pallet-system-frontend/try-runtime",
"snowbridge-runtime-common/try-runtime",
"sp-runtime/try-runtime",
"westend-runtime/try-runtime",
]
std = [
"alloy-core/std",
Expand Down Expand Up @@ -369,6 +380,7 @@ std = [
"parachain-info/std",
"parachains-common/std",
"polkadot-parachain-primitives/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
"primitive-types/std",
"scale-info/std",
Expand All @@ -378,8 +390,12 @@ std = [
"snowbridge-runtime-common/std",
"sp-api/std",
"sp-arithmetic/std",
"sp-authority-discovery/std",
"sp-block-builder/std",
"sp-consensus-aura/std",
"sp-consensus-babe/std",
"sp-consensus-beefy/std",
"sp-consensus-grandpa/std",
"sp-core/std",
"sp-genesis-builder/std",
"sp-inherents/std",
Expand Down
48 changes: 44 additions & 4 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod bag_thresholds;
pub mod governance;
#[cfg(not(feature = "runtime-benchmarks"))]
mod migrations;
mod staking;
pub mod staking;

use governance::{pallet_custom_origins, FellowshipAdmin, GeneralAdmin, StakingAdmin, Treasurer};

Expand Down Expand Up @@ -691,8 +691,8 @@ pub enum ProxyType {
Governance,
/// Allows access to staking related calls.
///
/// Contains the `Staking`, `Session`, `Utility`, `FastUnstake`, `VoterList`, `NominationPools`
/// pallets.
/// Contains the `Staking`, `StakingRcClient`, `Session`, `Utility`, `FastUnstake`,
/// `VoterList`, `NominationPools` pallets.
Staking,
/// Allows access to nomination pools related calls.
///
Expand Down Expand Up @@ -838,7 +838,10 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
matches!(
c,
RuntimeCall::Staking(..) |
RuntimeCall::Session(..) |
RuntimeCall::StakingRcClient(
pallet_staking_async_rc_client::Call::set_keys { .. } |
pallet_staking_async_rc_client::Call::purge_keys { .. }
) | RuntimeCall::Session(..) |
RuntimeCall::Utility(..) |
RuntimeCall::NominationPools(..) |
RuntimeCall::VoterList(..)
Expand Down Expand Up @@ -1678,6 +1681,9 @@ impl
}
}

#[cfg(feature = "runtime-benchmarks")]
type StakingRcClientBench<T> = pallet_staking_async_rc_client::benchmarking::Pallet<T>;

#[cfg(feature = "runtime-benchmarks")]
mod benches {
frame_benchmarking::define_benchmarks!(
Expand Down Expand Up @@ -1707,6 +1713,7 @@ mod benches {
[pallet_proxy, Proxy]
[pallet_session, SessionBench::<Runtime>]
[pallet_staking_async, Staking]
[pallet_staking_async_rc_client, StakingRcClientBench::<Runtime>]
[pallet_uniques, Uniques]
[pallet_utility, Utility]
[pallet_timestamp, Timestamp]
Expand Down Expand Up @@ -2233,6 +2240,39 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!(
(keys.keys, keys.proof.encode())
}
}

impl pallet_staking_async_rc_client::benchmarking::Config for Runtime {
fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Vec<u8>, Vec<u8>) {
use staking::RelayChainSessionKeys;
let keys = RelayChainSessionKeys::generate(&owner.encode(), None);
(keys.keys.encode(), keys.proof.encode())
}

fn setup_validator() -> Self::AccountId {
use frame_benchmarking::account;
use frame_support::traits::fungible::Mutate;

let stash: Self::AccountId = account("validator", 0, 0);
let balance = 10_000 * UNITS;

// Fund the account
let _ = Balances::mint_into(&stash, balance);

// Bond and validate
assert_ok!(Staking::bond(
RuntimeOrigin::signed(stash.clone()),
balance / 2,
pallet_staking_async::RewardDestination::Stash
));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(stash.clone()),
pallet_staking_async::ValidatorPrefs::default()
));

stash
}
}

use xcm_config::{MaxAssetsIntoHolding, WestendLocation};
use testnet_parachains_constants::westend::locations::{PeopleParaId, PeopleLocation};
parameter_types! {
Expand Down
112 changes: 111 additions & 1 deletion cumulus/parachains/runtimes/assets/asset-hub-westend/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,41 @@ impl pallet_staking_async::Config for Runtime {
type WeightInfo = weights::pallet_staking_async::WeightInfo<Runtime>;
}

// Relay Chain session keys type for validating session keys on AssetHub.
// This must match the exact structure of Westend's `SessionKeys` to ensure
// proper encoding/decoding compatibility.
sp_runtime::impl_opaque_keys! {
pub struct RelayChainSessionKeys {
pub grandpa: sp_consensus_grandpa::AuthorityId,
pub babe: sp_consensus_babe::AuthorityId,
pub para_validator: polkadot_primitives::ValidatorId,
pub para_assignment: polkadot_primitives::AssignmentId,
pub authority_discovery: sp_authority_discovery::AuthorityId,
pub beefy: sp_consensus_beefy::ecdsa_crypto::AuthorityId,
}
}

impl pallet_staking_async_rc_client::Config for Runtime {
type RelayChainOrigin = EnsureRoot<AccountId>;
type AHStakingInterface = Staking;
type SendToRelayChain = StakingXcmToRelayChain;
type MaxValidatorSetRetries = ConstU32<64>;
// export validator session at end of session 4 within an era.
type ValidatorSetExportSession = ConstU32<4>;
type SessionKeys = RelayChainSessionKeys;
// | Key | Crypto | Public Key | Signature |
// |---------------------|---------|------------|-----------|
// | grandpa | Ed25519 | 32 bytes | 64 bytes |
// | babe | Sr25519 | 32 bytes | 64 bytes |
// | para_validator | Sr25519 | 32 bytes | 64 bytes |
// | para_assignment | Sr25519 | 32 bytes | 64 bytes |
// | authority_discovery | Sr25519 | 32 bytes | 64 bytes |
// | beefy | ECDSA | 33 bytes | 65 bytes |
// | Total | | 193 bytes | 385 bytes |
// We add some buffer for SCALE encoding overhead and future expansions
type MaxSessionKeysLength = ConstU32<256>;
type MaxSessionKeysProofLength = ConstU32<512>;
type WeightInfo = ();
}

#[derive(Encode, Decode)]
Expand All @@ -321,9 +349,16 @@ pub enum RelayChainRuntimePallets {

#[derive(Encode, Decode)]
pub enum AhClientCalls {
// index of `fn validator_set` in `staking-async-ah-client`. It has only one call.
// index of `fn validator_set` in `staking-async-ah-client`.
#[codec(index = 0)]
ValidatorSet(rc_client::ValidatorSetReport<AccountId>),
// index of `fn set_keys_from_ah` in `staking-async-ah-client`.
// Note: proof is validated on AH side, so only keys are sent to RC.
#[codec(index = 3)]
SetKeys { stash: AccountId, keys: Vec<u8> },
// index of `fn purge_keys_from_ah` in `staking-async-ah-client`.
#[codec(index = 4)]
PurgeKeys { stash: AccountId },
}

pub struct ValidatorSetToXcm;
Expand All @@ -347,6 +382,63 @@ impl sp_runtime::traits::Convert<rc_client::ValidatorSetReport<AccountId>, Xcm<(
}
}

/// Message to set session keys on the Relay Chain.
/// Note: proof is validated on AH side, so only keys are sent to RC.
#[derive(Encode, Decode, Clone)]
pub struct SetKeysMessage {
pub stash: AccountId,
pub keys: Vec<u8>,
}

pub struct SetKeysToXcm;
impl sp_runtime::traits::Convert<SetKeysMessage, Xcm<()>> for SetKeysToXcm {
fn convert(msg: SetKeysMessage) -> Xcm<()> {
Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
fallback_max_weight: None,
call: RelayChainRuntimePallets::AhClient(AhClientCalls::SetKeys {
stash: msg.stash,
keys: msg.keys,
})
.encode()
.into(),
},
])
}
}

/// Message to purge session keys on the Relay Chain.
#[derive(Encode, Decode, Clone)]
pub struct PurgeKeysMessage {
pub stash: AccountId,
}

pub struct PurgeKeysToXcm;
impl sp_runtime::traits::Convert<PurgeKeysMessage, Xcm<()>> for PurgeKeysToXcm {
fn convert(msg: PurgeKeysMessage) -> Xcm<()> {
Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
fallback_max_weight: None,
call: RelayChainRuntimePallets::AhClient(AhClientCalls::PurgeKeys {
stash: msg.stash,
})
.encode()
.into(),
},
])
}
}

parameter_types! {
pub RelayLocation: Location = Location::parent();
}
Expand All @@ -363,6 +455,24 @@ impl rc_client::SendToRelayChain for StakingXcmToRelayChain {
ValidatorSetToXcm,
>::send(report)
}

fn set_keys(stash: Self::AccountId, keys: Vec<u8>) -> Result<(), ()> {
rc_client::XCMSender::<
xcm_config::XcmRouter,
RelayLocation,
SetKeysMessage,
SetKeysToXcm,
>::send(SetKeysMessage { stash, keys })
}

fn purge_keys(stash: Self::AccountId) -> Result<(), ()> {
rc_client::XCMSender::<
xcm_config::XcmRouter,
RelayLocation,
PurgeKeysMessage,
PurgeKeysToXcm,
>::send(PurgeKeysMessage { stash })
}
}

parameter_types! {
Expand Down
Loading
Loading