diff --git a/Cargo.lock b/Cargo.lock index 0cbb8d3e2b12a..fc596b9cedd9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13311,6 +13311,7 @@ dependencies = [ "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-staking", + "staging-xcm", ] [[package]] diff --git a/prdoc/pr_8422.prdoc b/prdoc/pr_8422.prdoc new file mode 100644 index 0000000000000..6d3046d48900e --- /dev/null +++ b/prdoc/pr_8422.prdoc @@ -0,0 +1,26 @@ +title: '[AHM] Staking async fixes for XCM and election planning' +doc: +- audience: Runtime Dev + description: |- + This PR brings a few small fixes related to the XCM messages of stkaing-async, among other small fixes: + + + * [x] Allows `xcm::validate` to check the message size, and we actually now act upon it in the `staking-async-rc/parachain-runtime`s. The code is a bit duplicate now, and there is a TOOD about how to better refactor it later. + * [x] Part of this work is backported separately as https://github.com/paritytech/polkadot-sdk/pull/8409 + * [x] It brings a default `EraElectionPlannerOf` which should be the right tool to use to ensure elections always happen in time, with an educated guess based on `ElectionProvider::duration` rather than a random number. + * [x] It adds a few unit tests about the above + * [x] It silences some logs that were needlessly `INFO`, and makes the printing of some types a bit more CLI friendly. + * [x] Renames `type SessionDuration` in `staking-async` to `type RelaySessionDuration` for better clarity. +crates: +- name: pallet-staking-async-ah-client + bump: patch +- name: pallet-staking-async-rc-client + bump: minor +- name: pallet-staking-async-parachain-runtime + bump: minor +- name: pallet-staking-async-rc-runtime + bump: major +- name: pallet-staking-async + bump: major +- name: pallet-election-provider-multi-block + bump: major diff --git a/substrate/frame/election-provider-multi-block/src/mock/signed.rs b/substrate/frame/election-provider-multi-block/src/mock/signed.rs index a11e737612c08..543e6021ea5fa 100644 --- a/substrate/frame/election-provider-multi-block/src/mock/signed.rs +++ b/substrate/frame/election-provider-multi-block/src/mock/signed.rs @@ -25,13 +25,13 @@ use crate::{ }; use frame_election_provider_support::PageIndex; use frame_support::{ - assert_ok, dispatch::PostDispatchInfo, parameter_types, traits::EstimateCallFee, BoundedVec, + assert_ok, dispatch::PostDispatchInfo, parameter_types, traits::EstimateCallFee, }; use sp_npos_elections::ElectionScore; use sp_runtime::{traits::Zero, Perbill}; parameter_types! { - pub static MockSignedNextSolution: Option, Pages>> = None; + pub static MockSignedNextSolution: Option>> = None; pub static MockSignedNextScore: Option = Default::default(); pub static MockSignedResults: Vec = Default::default(); } diff --git a/substrate/frame/election-provider-multi-block/src/types.rs b/substrate/frame/election-provider-multi-block/src/types.rs index 53215c1f27de4..867c09ffb1a19 100644 --- a/substrate/frame/election-provider-multi-block/src/types.rs +++ b/substrate/frame/election-provider-multi-block/src/types.rs @@ -82,14 +82,13 @@ pub type AssignmentOf = CloneNoBound, EqNoBound, PartialEqNoBound, - MaxEncodedLen, DefaultNoBound, )] #[codec(mel_bound(T: crate::Config))] #[scale_info(skip_type_params(T))] pub struct PagedRawSolution { /// The individual pages. - pub solution_pages: BoundedVec, ::Pages>, + pub solution_pages: Vec>, /// The final claimed score post feasibility and concatenation of all pages. pub score: ElectionScore, /// The designated round. @@ -165,6 +164,23 @@ pub trait PadSolutionPages: Sized { fn pad_solution_pages(self, desired_pages: PageIndex) -> Self; } +impl PadSolutionPages for Vec { + fn pad_solution_pages(self, desired_pages: PageIndex) -> Self { + let desired_pages_usize = desired_pages as usize; + debug_assert!(self.len() <= desired_pages_usize); + if self.len() == desired_pages_usize { + return self + } + + // we basically need to prepend the list with this many items. + let empty_slots = desired_pages_usize.saturating_sub(self.len()); + sp_std::iter::repeat(Default::default()) + .take(empty_slots) + .chain(self.into_iter()) + .collect::>() + } +} + impl> PadSolutionPages for BoundedVec { @@ -391,8 +407,6 @@ impl Phase { #[cfg(test)] mod pagify { use super::{PadSolutionPages, Pagify}; - use frame_support::{traits::ConstU32, BoundedVec}; - use sp_core::bounded_vec; #[test] fn pagify_works() { @@ -410,15 +424,11 @@ mod pagify { #[test] fn pad_solution_pages_works() { // noop if the solution is complete, as with pagify. - let solution: BoundedVec<_, ConstU32<3>> = bounded_vec![1u32, 2, 3]; - assert_eq!(solution.pad_solution_pages(3).into_inner(), vec![1, 2, 3]); + let solution = vec![1u32, 2, 3]; + assert_eq!(solution.pad_solution_pages(3), vec![1, 2, 3]); // pads the solution with default if partial.. - let solution: BoundedVec<_, ConstU32<3>> = bounded_vec![2, 3]; - assert_eq!(solution.pad_solution_pages(3).into_inner(), vec![0, 2, 3]); - - // behaves the same as `pad_solution_pages(3)`. - let solution: BoundedVec<_, ConstU32<3>> = bounded_vec![2, 3]; - assert_eq!(solution.pad_solution_pages(4).into_inner(), vec![0, 2, 3]); + let solution = vec![2, 3]; + assert_eq!(solution.pad_solution_pages(3), vec![0, 2, 3]); } } diff --git a/substrate/frame/election-provider-multi-block/src/unsigned/miner.rs b/substrate/frame/election-provider-multi-block/src/unsigned/miner.rs index bef8f86cfa1bc..692f302c605a5 100644 --- a/substrate/frame/election-provider-multi-block/src/unsigned/miner.rs +++ b/substrate/frame/election-provider-multi-block/src/unsigned/miner.rs @@ -371,7 +371,7 @@ impl BaseMiner { } // convert each page to a compact struct -- no more change allowed. - let solution_pages: BoundedVec, T::Pages> = paged_assignments + let mut solution_pages: Vec> = paged_assignments .into_iter() .enumerate() .map(|(page_index, assignment_page)| { @@ -382,12 +382,11 @@ impl BaseMiner { .ok_or(MinerError::SnapshotUnAvailable(SnapshotType::Voters(page)))?; // one last trimming -- `MaxBackersPerWinner`, the per-page variant. - let trimmed_assignment_page = - Self::trim_supports_max_backers_per_winner_per_page( - assignment_page, - voter_snapshot_page, - page_index as u32, - )?; + let trimmed_assignment_page = Self::trim_supports_max_backers_per_winner_per_page( + assignment_page, + voter_snapshot_page, + page_index as u32, + )?; let voter_index_fn = { let cache = helpers::generate_voter_cache::(&voter_snapshot_page); @@ -401,17 +400,11 @@ impl BaseMiner { ) .map_err::, _>(Into::into) }) - .collect::, _>>()? - .try_into() - .expect("`paged_assignments` is bound by `T::Pages`; length cannot change in iter chain; qed"); + .collect::, _>>()?; // now do the length trim. - let mut solution_pages_unbounded = solution_pages.into_inner(); let _trim_length_weight = - Self::maybe_trim_weight_and_len(&mut solution_pages_unbounded, &voter_pages)?; - let solution_pages = solution_pages_unbounded - .try_into() - .expect("maybe_trim_weight_and_len cannot increase the length of its input; qed."); + Self::maybe_trim_weight_and_len(&mut solution_pages, &voter_pages)?; miner_log!(debug, "trimmed {} voters due to length restriction.", _trim_length_weight); // finally, wrap everything up. Assign a fake score here, since we might need to re-compute diff --git a/substrate/frame/election-provider-multi-block/src/unsigned/mod.rs b/substrate/frame/election-provider-multi-block/src/unsigned/mod.rs index 5ab23b9e965f8..0f531043c2341 100644 --- a/substrate/frame/election-provider-multi-block/src/unsigned/mod.rs +++ b/substrate/frame/election-provider-multi-block/src/unsigned/mod.rs @@ -165,7 +165,7 @@ mod pallet { // we select the most significant pages, based on `T::MinerPages`. let page_indices = crate::Pallet::::msp_range_for(T::MinerPages::get() as usize); ::verify_synchronous_multi( - paged_solution.solution_pages.into_inner(), + paged_solution.solution_pages, page_indices, claimed_score, ) @@ -235,7 +235,12 @@ mod pallet { assert!( UnsignedWeightsOf::::submit_unsigned().all_lte(T::BlockWeights::get().max_block), "weight of `submit_unsigned` is too high" - ) + ); + assert!( + ::MinerPages::get() as usize <= + ::Pages::get() as usize, + "number of pages in the unsigned phase is too high" + ); } #[cfg(feature = "try-runtime")] @@ -333,6 +338,10 @@ mod pallet { paged_solution.solution_pages.len() == T::MinerPages::get() as usize, CommonError::WrongPageCount ); + ensure!( + paged_solution.solution_pages.len() <= ::Pages::get() as usize, + CommonError::WrongPageCount + ); Ok(()) } diff --git a/substrate/frame/election-provider-multi-block/src/verifier/tests.rs b/substrate/frame/election-provider-multi-block/src/verifier/tests.rs index 08f67f27d5fb5..83f4e428b1be1 100644 --- a/substrate/frame/election-provider-multi-block/src/verifier/tests.rs +++ b/substrate/frame/election-provider-multi-block/src/verifier/tests.rs @@ -927,7 +927,7 @@ mod multi_page_sync_verification { assert_eq!(::queued_score(), None); let _ = ::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(2), paged.score, ) @@ -955,7 +955,7 @@ mod multi_page_sync_verification { assert_eq!(::queued_score(), None); let _ = ::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(3), paged.score, ) @@ -987,7 +987,7 @@ mod multi_page_sync_verification { assert_eq!( ::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(2), paged.score, ) @@ -1021,7 +1021,7 @@ mod multi_page_sync_verification { assert_eq!( ::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(2), paged.score, ) @@ -1055,7 +1055,7 @@ mod multi_page_sync_verification { hypothetically!({ assert_ok!(::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(2), paged.score, )); @@ -1098,7 +1098,7 @@ mod multi_page_sync_verification { assert_eq!( ::verify_synchronous_multi( - paged.solution_pages.clone().into_inner(), + paged.solution_pages.clone(), MultiBlock::msp_range_for(2), paged.score, ) diff --git a/substrate/frame/staking-async/ah-client/src/lib.rs b/substrate/frame/staking-async/ah-client/src/lib.rs index 910f0c7cb90a2..eedfc2f814298 100644 --- a/substrate/frame/staking-async/ah-client/src/lib.rs +++ b/substrate/frame/staking-async/ah-client/src/lib.rs @@ -430,7 +430,7 @@ pub mod pallet { report: rc_client::ValidatorSetReport, ) -> DispatchResult { // Ensure the origin is one of Root or whatever is representing AssetHub. - log!(info, "Received new validator set report {:?}", report); + log!(debug, "Received new validator set report {}", report); T::AssetHubOrigin::ensure_origin_or_root(origin)?; // Check the operating mode. diff --git a/substrate/frame/staking-async/ahm-test/src/ah/mock.rs b/substrate/frame/staking-async/ahm-test/src/ah/mock.rs index 9f0054f67a1c3..4d9691b565814 100644 --- a/substrate/frame/staking-async/ahm-test/src/ah/mock.rs +++ b/substrate/frame/staking-async/ahm-test/src/ah/mock.rs @@ -317,7 +317,7 @@ parameter_types! { pub static BondingDuration: u32 = 3; pub static SlashDeferredDuration: u32 = 2; pub static SessionsPerEra: u32 = 6; - pub static PlanningEraOffset: u32 = 1; + pub static PlanningEraOffset: u32 = 2; } impl pallet_staking_async::Config for Runtime { diff --git a/substrate/frame/staking-async/rc-client/Cargo.toml b/substrate/frame/staking-async/rc-client/Cargo.toml index c555cfd9a6471..da2bbdf93173e 100644 --- a/substrate/frame/staking-async/rc-client/Cargo.toml +++ b/substrate/frame/staking-async/rc-client/Cargo.toml @@ -17,6 +17,7 @@ scale-info = { workspace = true, features = ["derive"] } sp-core = { workspace = true } sp-runtime = { features = ["serde"], workspace = true } sp-staking = { features = ["serde"], workspace = true } +xcm = { workspace = true } [features] default = ["std"] @@ -29,12 +30,14 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-staking/std", + "xcm/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "xcm/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/substrate/frame/staking-async/rc-client/src/lib.rs b/substrate/frame/staking-async/rc-client/src/lib.rs index db09a4baa388c..28138bdbcb27a 100644 --- a/substrate/frame/staking-async/rc-client/src/lib.rs +++ b/substrate/frame/staking-async/rc-client/src/lib.rs @@ -116,10 +116,12 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; +use core::fmt::Display; use frame_support::pallet_prelude::*; -use sp_runtime::Perbill; +use sp_runtime::{traits::Convert, Perbill}; use sp_staking::SessionIndex; +use xcm::latest::{send_xcm, Location, SendError, SendXcm, Xcm}; /// Export everything needed for the pallet to be used in the runtime. pub use pallet::*; @@ -152,7 +154,7 @@ pub trait SendToRelayChain { fn validator_set(report: ValidatorSetReport); } -#[derive(Encode, Decode, DecodeWithMemTracking, Debug, Clone, PartialEq, TypeInfo)] +#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, TypeInfo)] /// A report about a new validator set. This is sent from AH -> RC. pub struct ValidatorSetReport { /// The new validator set. @@ -174,6 +176,28 @@ pub struct ValidatorSetReport { pub leftover: bool, } +impl core::fmt::Debug for ValidatorSetReport { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ValidatorSetReport") + .field("new_validator_set", &self.new_validator_set) + .field("id", &self.id) + .field("prune_up_to", &self.prune_up_to) + .field("leftover", &self.leftover) + .finish() + } +} + +impl core::fmt::Display for ValidatorSetReport { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ValidatorSetReport") + .field("new_validator_set", &self.new_validator_set.len()) + .field("id", &self.id) + .field("prune_up_to", &self.prune_up_to) + .field("leftover", &self.leftover) + .finish() + } +} + impl ValidatorSetReport { /// A new instance of self that is terminal. This is useful when we want to send everything in /// one go. @@ -196,7 +220,7 @@ impl ValidatorSetReport { Ok(self) } - /// Split self into `count` number of pieces. + /// Split self into chunks of `chunk_size` element. pub fn split(self, chunk_size: usize) -> Vec where AccountId: Clone, @@ -213,9 +237,7 @@ impl ValidatorSetReport { } } -#[derive( - Encode, Decode, DecodeWithMemTracking, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, -)] +#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, TypeInfo, MaxEncodedLen)] /// The information that is sent from RC -> AH on session end. pub struct SessionReport { /// The session that is ending. @@ -246,6 +268,28 @@ pub struct SessionReport { pub leftover: bool, } +impl core::fmt::Debug for SessionReport { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("SessionReport") + .field("end_index", &self.end_index) + .field("validator_points", &self.validator_points) + .field("activation_timestamp", &self.activation_timestamp) + .field("leftover", &self.leftover) + .finish() + } +} + +impl core::fmt::Display for SessionReport { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("SessionReport") + .field("end_index", &self.end_index) + .field("validator_points", &self.validator_points.len()) + .field("activation_timestamp", &self.activation_timestamp) + .field("leftover", &self.leftover) + .finish() + } +} + impl SessionReport { /// A new instance of self that is terminal. This is useful when we want to send everything in /// one go. @@ -287,6 +331,137 @@ impl SessionReport { } } +/// A trait to encapsulate messages between RC and AH that can be splitted into smaller chunks. +/// +/// Implemented for [`SessionReport`] and [`ValidatorSetReport`]. +#[allow(clippy::len_without_is_empty)] +pub trait SplittableMessage: Sized { + /// Split yourself into pieces of `chunk_size` size. + fn split_by(self, chunk_size: usize) -> Vec; + + /// Current length of the message. + fn len(&self) -> usize; +} + +impl SplittableMessage for SessionReport { + fn split_by(self, chunk_size: usize) -> Vec { + self.split(chunk_size) + } + fn len(&self) -> usize { + self.validator_points.len() + } +} + +impl SplittableMessage for ValidatorSetReport { + fn split_by(self, chunk_size: usize) -> Vec { + self.split(chunk_size) + } + fn len(&self) -> usize { + self.new_validator_set.len() + } +} + +/// Common utility to send XCM messages that can use [`SplittableMessage`]. +/// +/// It can be used both in the RC and AH. `Message` is the splittable message type, and `ToXcm` +/// should be configured by the user, converting `message` to a valida `Xcm<()>`. It should utilize +/// the correct call indices, which we only know at the runtime level. +pub struct XCMSender( + core::marker::PhantomData<(Sender, Destination, Message, ToXcm)>, +); + +impl XCMSender +where + Sender: SendXcm, + Destination: Get, + Message: SplittableMessage + Display + Clone + Encode, + ToXcm: Convert>, +{ + /// Safe send method to send a `message`, while validating it and using [`SplittableMessage`] to + /// split it into smaller pieces if XCM validation fails with `ExceedsMaxMessageSize`. It will + /// fail on other errors. + /// + /// It will only emit some logs, and has no return value. This is used in the runtime, so it + /// cannot deposit any events at this level. + pub fn split_then_send(message: Message, maybe_max_steps: Option) { + let message_type_name = core::any::type_name::(); + let dest = Destination::get(); + let xcms = match Self::prepare(message, maybe_max_steps) { + Ok(x) => x, + Err(e) => { + log::error!(target: "runtime::rc-client", "📨 Failed to split message {}: {:?}", message_type_name, e); + return; + }, + }; + + for (idx, xcm) in xcms.into_iter().enumerate() { + log::debug!(target: "runtime::rc-client", "📨 sending {} message index {}, size: {:?}", message_type_name, idx, xcm.encoded_size()); + let result = send_xcm::(dest.clone(), xcm); + match result { + Ok(_) => { + log::debug!(target: "runtime::rc-client", "📨 Successfully sent {} message part {} to relay chain", message_type_name, idx) + }, + Err(e) => { + log::error!(target: "runtime::rc-client", "📨 Failed to send {} message to relay chain: {:?}", message_type_name, e) + }, + } + } + } + + fn prepare(message: Message, maybe_max_steps: Option) -> Result>, SendError> { + // initial chunk size is the entire thing, so it will be a vector of 1 item. + let mut chunk_size = message.len(); + let mut steps = 0; + + loop { + let current_messages = message.clone().split_by(chunk_size); + + // the first message is the heaviest, the last one might be smaller. + let first_message = if let Some(r) = current_messages.first() { + r + } else { + log::debug!(target: "runtime::staking-async::xcm", "📨 unexpected: no messages to send"); + return Ok(vec![]); + }; + + log::debug!( + target: "runtime::staking-async::xcm", + "📨 step: {:?}, chunk_size: {:?}, message_size: {:?}", + steps, + chunk_size, + first_message.encoded_size(), + ); + + let first_xcm = ToXcm::convert(first_message.clone()); + match ::validate(&mut Some(Destination::get()), &mut Some(first_xcm)) + { + Ok((_ticket, price)) => { + log::debug!(target: "runtime::staking-async::xcm", "📨 validated, price: {:?}", price); + return Ok(current_messages.into_iter().map(ToXcm::convert).collect::>()); + }, + Err(SendError::ExceedsMaxMessageSize) => { + log::debug!(target: "runtime::staking-async::xcm", "📨 ExceedsMaxMessageSize -- reducing chunk_size"); + chunk_size = chunk_size.saturating_div(2); + steps += 1; + if maybe_max_steps.is_some_and(|max_steps| steps > max_steps) || + chunk_size.is_zero() + { + log::error!(target: "runtime::staking-async::xcm", "📨 Exceeded max steps or chunk_size = 0"); + return Err(SendError::ExceedsMaxMessageSize); + } else { + // try again with the new `chunk_size` + continue; + } + }, + Err(other) => { + log::error!(target: "runtime::staking-async::xcm", "📨 other error -- cannot send XCM: {:?}", other); + return Err(other); + }, + } + } + } +} + /// Our communication trait of `pallet-staking-async-rc-client` -> `pallet-staking-async`. /// /// This is merely a shorthand to avoid tightly-coupling the staking pallet to this pallet. It @@ -435,7 +610,7 @@ pub mod pallet { origin: OriginFor, report: SessionReport, ) -> DispatchResult { - log!(info, "Received session report: {:?}", report); + log!(debug, "Received session report: {}", report); T::RelayChainOrigin::ensure_origin_or_root(origin)?; match LastSessionReportEndingIndex::::get() { diff --git a/substrate/frame/staking-async/runtimes/parachain/Cargo.toml b/substrate/frame/staking-async/runtimes/parachain/Cargo.toml index a4df251d7c63e..029fbceec1633 100644 --- a/substrate/frame/staking-async/runtimes/parachain/Cargo.toml +++ b/substrate/frame/staking-async/runtimes/parachain/Cargo.toml @@ -88,7 +88,11 @@ sp-transaction-pool = { workspace = true } sp-version = { workspace = true } # num-traits feature needed for dex integer sq root: -primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } +primitive-types = { features = [ + "codec", + "num-traits", + "scale-info", +], workspace = true } # Polkadot pallet-xcm = { workspace = true } diff --git a/substrate/frame/staking-async/runtimes/parachain/Staking-Justfile b/substrate/frame/staking-async/runtimes/parachain/Staking-Justfile new file mode 100644 index 0000000000000..cbb9a4c2c0d29 --- /dev/null +++ b/substrate/frame/staking-async/runtimes/parachain/Staking-Justfile @@ -0,0 +1,37 @@ +default: build create-specs launch + +# Set default values for the named preset +named-preset := "development" +log := "runtime::multiblock-election=info,runtime::staking=info" +para-id := "1100" +relay-chain := "rococo-local" +parachain-runtime-path := "../../../../../target/release/wbuild/pallet-staking-async-parachain-runtime/pallet_staking_async_parachain_runtime.compact.compressed.wasm" +rc-runtime-path := "../../../../../target/release/wbuild/pallet-staking-async-rc-runtime/fast_runtime_binary.rs.wasm" + + +build: + RUST_LOG={{log}} cargo build --release -p pallet-staking-async-rc-runtime -p pallet-staking-async-parachain-runtime -p staging-chain-spec-builder + +create-specs: + rm -f ./parachain.json ./rc.json + + RUST_LOG={{log}} ../../../../../target/release/chain-spec-builder \ + create \ + -t development \ + --runtime {{parachain-runtime-path}} \ + --relay-chain {{relay-chain}} \ + --para-id {{para-id}} \ + named-preset {{named-preset}} + mv ./chain_spec.json ./parachain.json + + RUST_LOG={{log}} ../../../../../target/release/chain-spec-builder \ + create \ + -t development \ + --runtime {{rc-runtime-path}} \ + named-preset local_testnet + mv ./chain_spec.json ./rc.json + +launch: + zombienet --provider native -l text spawn zombienet-staking-runtimes.toml + +no-compile: create-specs launch diff --git a/substrate/frame/staking-async/runtimes/parachain/build-and-run-zn.sh b/substrate/frame/staking-async/runtimes/parachain/build-and-run-zn.sh index 721c672662215..00583af09aedb 100755 --- a/substrate/frame/staking-async/runtimes/parachain/build-and-run-zn.sh +++ b/substrate/frame/staking-async/runtimes/parachain/build-and-run-zn.sh @@ -19,10 +19,8 @@ RUST_LOG=${LOG} ../../../../../target/release/chain-spec-builder \ --runtime ../../../../../target/release/wbuild/pallet-staking-async-parachain-runtime/pallet_staking_async_parachain_runtime.compact.compressed.wasm \ --relay-chain rococo-local \ --para-id 1100 \ - named-preset dot_size - # named-preset ksm_size - # named-preset development - # change this as per your needs ^^^ + named-preset development + # change this as per your needs ^^^ options: development / dot_size / ksm_size mv ./chain_spec.json ./parachain.json echo "✅ creating rc chain specs" diff --git a/substrate/frame/staking-async/runtimes/parachain/src/lib.rs b/substrate/frame/staking-async/runtimes/parachain/src/lib.rs index db7c967bbc1b3..e069e032f3c9a 100644 --- a/substrate/frame/staking-async/runtimes/parachain/src/lib.rs +++ b/substrate/frame/staking-async/runtimes/parachain/src/lib.rs @@ -38,7 +38,9 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use assets_common::{ + foreign_creators::ForeignCreators, local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, + matching::{FromNetwork, FromSiblingParachain}, AssetIdForPoolAssets, AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert, }; use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; @@ -74,8 +76,11 @@ use parachains_common::{ BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; +use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; use sp_runtime::{ generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, ConvertInto, Verify}, @@ -88,25 +93,16 @@ use sp_version::RuntimeVersion; use testnet_parachains_constants::westend::{ consensus::*, currency::*, fee::WeightToFee, snowbridge::EthereumNetwork, time::*, }; -use xcm_config::{ - ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, - PoolAssetsPalletLocation, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, -}; - -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; - -use assets_common::{ - foreign_creators::ForeignCreators, - matching::{FromNetwork, FromSiblingParachain}, -}; -use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{ latest::prelude::AssetId, prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, }; +use xcm_config::{ + ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, + PoolAssetsPalletLocation, TrustBackedAssetsConvertedConcreteId, + TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, +}; #[cfg(feature = "runtime-benchmarks")] use frame_support::traits::PalletInfoAccess; @@ -130,14 +126,10 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - // Note: "westmint" is the legacy name for this chain. It has been renamed to - // "asset-hub-next-westend". Many wallets/tools depend on the `spec_name`, so it remains - // "westmint" for the time being. Wallets/tools should update to treat "asset-hub-next-westend" - // equally. - spec_name: alloc::borrow::Cow::Borrowed("asset-hub-next"), - impl_name: alloc::borrow::Cow::Borrowed("asset-hub-next"), + spec_name: alloc::borrow::Cow::Borrowed("staking-async-parachain"), + impl_name: alloc::borrow::Cow::Borrowed("staking-async-parachain"), authoring_version: 1, - spec_version: 1_017_007, + spec_version: 1_000_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, diff --git a/substrate/frame/staking-async/runtimes/parachain/src/staking.rs b/substrate/frame/staking-async/runtimes/parachain/src/staking.rs index d57a6ee31b282..389f9fd1dc923 100644 --- a/substrate/frame/staking-async/runtimes/parachain/src/staking.rs +++ b/substrate/frame/staking-async/runtimes/parachain/src/staking.rs @@ -15,8 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::marker::PhantomData; - ///! Staking, and election related pallet configurations. use super::*; use cumulus_primitives_core::relay_chain::SessionIndex; @@ -26,18 +24,20 @@ use pallet_election_provider_multi_block::{ self as multi_block, weights::measured, SolutionAccuracyOf, }; use pallet_staking_async::UseValidatorsMap; +use pallet_staking_async_rc_client as rc_client; use polkadot_runtime_common::{prod_or_fast, BalanceToU256, U256ToBalance}; use sp_runtime::{ - transaction_validity::TransactionPriority, FixedPointNumber, FixedU128, SaturatedConversion, + traits::Convert, transaction_validity::TransactionPriority, FixedPointNumber, FixedU128, + SaturatedConversion, }; +use xcm::latest::prelude::*; parameter_types! { - pub storage SignedPhase: u32 = 3 * MINUTES; - pub storage UnsignedPhase: u32 = 1 * MINUTES; + pub storage SignedPhase: u32 = 2 * MINUTES; + pub storage UnsignedPhase: u32 = MINUTES; pub storage SignedValidationPhase: u32 = Pages::get() + 1; - /// Compatible with Polkadot, we allow up to 22_500 nominators to be considered for election - pub storage MaxElectingVoters: u32 = 2000; + pub storage MaxElectingVoters: u32 = 1000; /// Maximum number of validators that we may want to elect. 1000 is the end target. pub const MaxValidatorSet: u32 = 1000; @@ -58,7 +58,7 @@ parameter_types! { pub MaxBackersPerWinner: u32 = VoterSnapshotPerBlock::get(); /// Total number of backers per winner across all pages. This is not used in the code yet. - pub MaxBackersPerWinnerFinal: u32 = MaxBackersPerWinner::get(); + pub MaxBackersPerWinnerFinal: u32 = MaxElectingVoters::get(); /// Size of the exposures. This should be small enough to make the reward payouts feasible. pub const MaxExposurePageSize: u32 = 64; @@ -237,6 +237,8 @@ impl pallet_staking_async::EraPayout for EraPayout { parameter_types! { // Six sessions in an era (6 hours). pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 1); + /// Duration of a relay session in our blocks. Needs to be hardcoded per-runtime. + pub const RelaySessionDuration: BlockNumber = 10; // 2 eras for unbonding (12 hours). pub const BondingDuration: sp_staking::EraIndex = 2; // 1 era in which slashes can be cancelled (6 hours). @@ -276,14 +278,19 @@ impl pallet_staking_async::Config for Runtime { type WeightInfo = weights::pallet_staking_async::WeightInfo; type MaxInvulnerables = frame_support::traits::ConstU32<20>; type MaxDisabledValidators = ConstU32<100>; - type PlanningEraOffset = ConstU32<2>; + type PlanningEraOffset = + pallet_staking_async::PlanningEraOffsetOf>; type RcClientInterface = StakingNextRcClient; } impl pallet_staking_async_rc_client::Config for Runtime { type RelayChainOrigin = EnsureRoot; type AHStakingInterface = Staking; - type SendToRelayChain = XcmToRelayChain; + type SendToRelayChain = StakingXcmToRelayChain; +} + +parameter_types! { + pub StakingXcmDestination: Location = Location::parent(); } #[derive(Encode, Decode)] @@ -299,16 +306,10 @@ pub enum AhClientCalls { ValidatorSet(rc_client::ValidatorSetReport), } -use pallet_staking_async_rc_client as rc_client; -use xcm::latest::{prelude::*, SendXcm}; - -pub struct XcmToRelayChain(PhantomData); -impl rc_client::SendToRelayChain for XcmToRelayChain { - type AccountId = AccountId; - - /// Send a new validator set report to relay chain. - fn validator_set(report: rc_client::ValidatorSetReport) { - let message = Xcm(vec![ +pub struct ValidatorSetToXcm; +impl Convert, Xcm<()>> for ValidatorSetToXcm { + fn convert(report: rc_client::ValidatorSetReport) -> Xcm<()> { + Xcm(vec![ Instruction::UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None, @@ -320,18 +321,21 @@ impl rc_client::SendToRelayChain for XcmToRelayChain { .encode() .into(), }, - ]); - let dest = Location::parent(); - let result = send_xcm::(dest, message); + ]) + } +} - match result { - Ok(_) => { - log::info!(target: "runtime", "Successfully sent validator set report to relay chain") - }, - Err(e) => { - log::error!(target: "runtime", "Failed to send validator set report to relay chain: {:?}", e) - }, - } +pub struct StakingXcmToRelayChain; + +impl rc_client::SendToRelayChain for StakingXcmToRelayChain { + type AccountId = AccountId; + fn validator_set(report: rc_client::ValidatorSetReport) { + rc_client::XCMSender::< + xcm_config::XcmRouter, + StakingXcmDestination, + rc_client::ValidatorSetReport, + ValidatorSetToXcm, + >::split_then_send(report, Some(8)); } } diff --git a/substrate/frame/staking-async/runtimes/parachain/src/weights/pallet_staking_async.rs b/substrate/frame/staking-async/runtimes/parachain/src/weights/pallet_staking_async.rs index b17e367a85154..3d69b2947be05 100644 --- a/substrate/frame/staking-async/runtimes/parachain/src/weights/pallet_staking_async.rs +++ b/substrate/frame/staking-async/runtimes/parachain/src/weights/pallet_staking_async.rs @@ -32,7 +32,7 @@ // --pallet // pallet_staking_async // --extrinsic -// +// // --output // substrate/frame/staking-async/runtimes/parachain/src/weights/pallet_staking_async.rs @@ -481,8 +481,8 @@ impl pallet_staking_async::WeightInfo for WeightInfo } /// Storage: `Staking::ErasStakersOverview` (r:1 w:0) /// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) - /// Storage: `Staking::ErasClaimedRewards` (r:1 w:1) - /// Proof: `Staking::ErasClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Staking::ClaimedRewards` (r:1 w:1) + /// Proof: `Staking::ClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0) diff --git a/substrate/frame/staking-async/runtimes/parachain/zombienet-staking-runtimes.toml b/substrate/frame/staking-async/runtimes/parachain/zombienet-staking-runtimes.toml index 8cf6c5946dcf4..57d8b7a6da872 100644 --- a/substrate/frame/staking-async/runtimes/parachain/zombienet-staking-runtimes.toml +++ b/substrate/frame/staking-async/runtimes/parachain/zombienet-staking-runtimes.toml @@ -10,9 +10,9 @@ rpc_port = 9944 [[relaychain.nodes]] name = "bob" validator = true -rpc_port = 9955 +rpc_port = 9945 args = [ - "-lruntime::system=debug,runtime::session=trace,runtime::staking::ah-client=trace", + "-lruntime::system=debug,runtime::session=trace,runtime::staking::ah-client=trace,runtime::ah-client=debug", ] [[parachains]] @@ -21,7 +21,7 @@ chain_spec_path = "./parachain.json" [parachains.collator] name = "charlie" -rpc_port = 9966 +rpc_port = 9946 args = [ - "-lruntime::system=debug,runtime::multiblock-election=debug,runtime::staking=debug,runtime::staking::rc-client=trace", + "-lruntime::system=debug,runtime::multiblock-election=trace,runtime::staking=debug,runtime::staking::rc-client=trace,runtime::rc-client=debug", ] diff --git a/substrate/frame/staking-async/runtimes/rc/src/genesis_config_presets.rs b/substrate/frame/staking-async/runtimes/rc/src/genesis_config_presets.rs index 7140d53d29359..73906b890c64d 100644 --- a/substrate/frame/staking-async/runtimes/rc/src/genesis_config_presets.rs +++ b/substrate/frame/staking-async/runtimes/rc/src/genesis_config_presets.rs @@ -17,8 +17,8 @@ //! Genesis configs presets for the Westend runtime use crate::{ - BabeConfig, BalancesConfig, ConfigurationConfig, RegistrarConfig, RuntimeGenesisConfig, - SessionConfig, SessionKeys, StakingAsyncAhClientConfig, SudoConfig, BABE_GENESIS_EPOCH_CONFIG, + AssetHubStakingClientConfig, BabeConfig, BalancesConfig, ConfigurationConfig, RegistrarConfig, + RuntimeGenesisConfig, SessionConfig, SessionKeys, SudoConfig, BABE_GENESIS_EPOCH_CONFIG, }; #[cfg(not(feature = "std"))] use alloc::format; @@ -107,6 +107,7 @@ fn default_parachains_host_configuration( max_head_data_size: 32 * 1024, max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, + // NOTE: these can be tweaked to mimic the XCM message splitting. max_downward_message_size: 1024 * 1024, max_upward_message_size: 50 * 1024, max_upward_message_num_per_candidate: 5, @@ -196,7 +197,7 @@ fn westend_testnet_genesis( sudo: SudoConfig { key: Some(root_key) }, configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID }, - staking_async_ah_client: StakingAsyncAhClientConfig { + asset_hub_staking_client: AssetHubStakingClientConfig { operating_mode: pallet_staking_async_ah_client::OperatingMode::Active, ..Default::default() } diff --git a/substrate/frame/staking-async/runtimes/rc/src/lib.rs b/substrate/frame/staking-async/runtimes/rc/src/lib.rs index b5acae3c0ba8a..c75e04cb7c826 100644 --- a/substrate/frame/staking-async/runtimes/rc/src/lib.rs +++ b/substrate/frame/staking-async/runtimes/rc/src/lib.rs @@ -35,7 +35,6 @@ use frame_support::{ derive_impl, dynamic_params::{dynamic_pallet_params, dynamic_params}, genesis_builder_helper::{build_state, get_preset}, - pallet_prelude::PhantomData, parameter_types, traits::{ fungible::HoldConsideration, tokens::UnityOrOuterConversion, ConstBool, ConstU32, Contains, @@ -106,8 +105,8 @@ pub use sp_runtime::BuildStorage; use sp_runtime::{ generic, impl_opaque_keys, traits::{ - AccountIdConversion, BlakeTwo256, Block as BlockT, ConvertInto, Get, IdentityLookup, - Keccak256, OpaqueKeys, SaturatedConversion, Verify, + AccountIdConversion, BlakeTwo256, Block as BlockT, Convert, ConvertInto, Get, + IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill, @@ -166,8 +165,8 @@ pub mod fast_runtime_binary { /// Runtime version (Westend). #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: alloc::borrow::Cow::Borrowed("westend-next"), - impl_name: alloc::borrow::Cow::Borrowed("parity-westend"), + spec_name: alloc::borrow::Cow::Borrowed("staking-async-rc"), + impl_name: alloc::borrow::Cow::Borrowed("staking-async-rc"), authoring_version: 2, spec_version: 1_017_001, impl_version: 0, @@ -498,7 +497,7 @@ impl pallet_timestamp::Config for Runtime { impl pallet_authorship::Config for Runtime { type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type EventHandler = StakingAsyncAhClient; + type EventHandler = AssetHubStakingClient; } parameter_types! { @@ -525,7 +524,7 @@ impl sp_runtime::traits::Convert> for IdentityValid } /// A testing type that implements SessionManager, it receives a new validator set from -/// `StakingAsyncAhClient`, but it prevents them from being passed over to the session pallet and +/// `AssetHubStakingClient`, but it prevents them from being passed over to the session pallet and /// just uses the previous session keys. pub struct AckButPreviousSessionValidatorsPersist(core::marker::PhantomData); @@ -560,7 +559,7 @@ impl pallet_session::Config for Runtime { type ShouldEndSession = Babe; type NextSessionRotation = Babe; type SessionManager = AckButPreviousSessionValidatorsPersist< - session_historical::NoteHistoricalRoot, + session_historical::NoteHistoricalRoot, >; type SessionHandler = ::KeyTypeIdProviders; type Keys = SessionKeys; @@ -577,49 +576,40 @@ impl session_historical::Config for Runtime { pub struct AssetHubLocation; impl Get for AssetHubLocation { fn get() -> Location { - Location::new(0, [Junction::Parachain(1000)]) - } -} - -pub struct AssetHubNextLocation; -impl Get for AssetHubNextLocation { - fn get() -> Location { - // TODO: once we are done with AH-next, replace with original AH id. Location::new(0, [Junction::Parachain(1100)]) } } -#[derive(Encode, Decode)] -enum AssetHubRuntimePallets { - #[codec(index = 89)] - RcClient(RcClientCalls), -} - -/// Call encoding for the calls needed from the rc-client pallet. -#[derive(Encode, Decode)] -enum RcClientCalls { - /// A session with the given index has started. - #[codec(index = 0)] - RelaySessionReport(rc_client::SessionReport), - #[codec(index = 1)] - RelayNewOffence(SessionIndex, Vec>), +pub struct SessionReportToXcm; +impl Convert, Xcm<()>> for SessionReportToXcm { + fn convert(a: rc_client::SessionReport) -> Xcm<()> { + Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + Instruction::Transact { + origin_kind: OriginKind::Superuser, + fallback_max_weight: None, + call: AssetHubRuntimePallets::RcClient(RcClientCalls::RelaySessionReport(a)) + .encode() + .into(), + }, + ]) + } } -pub struct XcmToAssetHub>(PhantomData<(T, AssetHubId)>); -impl> ah_client::SendToAssetHub for XcmToAssetHub { +pub struct StakingXcmToAssetHub; +impl ah_client::SendToAssetHub for StakingXcmToAssetHub { type AccountId = AccountId; fn relay_session_report(session_report: rc_client::SessionReport) { - let message = Xcm(vec![ - Instruction::UnpaidExecution { - weight_limit: WeightLimit::Unlimited, - check_origin: None, - }, - Self::mk_asset_hub_call(RcClientCalls::RelaySessionReport(session_report)), - ]); - if let Err(err) = send_xcm::(AssetHubNextLocation::get(), message) { - log::error!(target: "runtime", "Failed to send relay session report message: {:?}", err); - } + rc_client::XCMSender::< + xcm_config::XcmRouter, + AssetHubLocation, + rc_client::SessionReport, + SessionReportToXcm, + >::split_then_send(session_report, Some(8)); } fn relay_new_offence( @@ -631,24 +621,37 @@ impl> ah_client::SendToAssetHub for XcmToAssetH weight_limit: WeightLimit::Unlimited, check_origin: None, }, - Self::mk_asset_hub_call(RcClientCalls::RelayNewOffence(session_index, offences)), + Instruction::Transact { + origin_kind: OriginKind::Superuser, + fallback_max_weight: None, + call: AssetHubRuntimePallets::RcClient(RcClientCalls::RelayNewOffence( + session_index, + offences, + )) + .encode() + .into(), + }, ]); - if let Err(err) = send_xcm::(AssetHubNextLocation::get(), message) { - log::error!(target: "runtime", "Failed to send relay offence message: {:?}", err); + if let Err(err) = send_xcm::(AssetHubLocation::get(), message) { + log::error!(target: "runtime::ah-client", "Failed to send relay offence message: {:?}", err); } } } -impl> XcmToAssetHub { - fn mk_asset_hub_call( - call: RcClientCalls<::AccountId>, - ) -> Instruction<()> { - Instruction::Transact { - origin_kind: OriginKind::Superuser, - fallback_max_weight: None, - call: AssetHubRuntimePallets::RcClient(call).encode().into(), - } - } +#[derive(Encode, Decode)] +enum AssetHubRuntimePallets { + #[codec(index = 89)] + RcClient(RcClientCalls), +} + +/// Call encoding for the calls needed from the rc-client pallet. +#[derive(Encode, Decode)] +enum RcClientCalls { + /// A session with the given index has started. + #[codec(index = 0)] + RelaySessionReport(rc_client::SessionReport), + #[codec(index = 1)] + RelayNewOffence(SessionIndex, Vec>), } pub struct EnsureAssetHub; @@ -675,8 +678,8 @@ impl pallet_staking_async_ah_client::Config for Runtime { frame_support::traits::EitherOfDiverse, EnsureAssetHub>; type AdminOrigin = EnsureRoot; type SessionInterface = Self; - type SendToAssetHub = XcmToAssetHub; - type MinimumValidatorSetSize = ConstU32<333>; + type SendToAssetHub = StakingXcmToAssetHub; + type MinimumValidatorSetSize = ConstU32<10>; type UnixTime = Timestamp; type PointsPerBlock = ConstU32<20>; type Fallback = Staking; @@ -938,7 +941,7 @@ impl pallet_treasury::Config for Runtime { impl pallet_offences::Config for Runtime { type RuntimeEvent = RuntimeEvent; type IdentificationTuple = session_historical::IdentificationTuple; - type OnOffenceHandler = StakingAsyncAhClient; + type OnOffenceHandler = AssetHubStakingClient; } impl pallet_authority_discovery::Config for Runtime { @@ -1333,7 +1336,7 @@ impl parachains_inclusion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DisputesHandler = ParasDisputes; type RewardValidators = - parachains_reward_points::RewardValidatorsWithEraPoints; + parachains_reward_points::RewardValidatorsWithEraPoints; type MessageQueue = MessageQueue; type WeightInfo = weights::polkadot_runtime_parachains_inclusion::WeightInfo; } @@ -1510,7 +1513,7 @@ impl assigned_slots::Config for Runtime { impl parachains_disputes::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RewardValidators = - parachains_reward_points::RewardValidatorsWithEraPoints; + parachains_reward_points::RewardValidatorsWithEraPoints; type SlashingHandler = parachains_slashing::SlashValidatorsForDisputes; type WeightInfo = weights::polkadot_runtime_parachains_disputes::WeightInfo; } @@ -1819,7 +1822,7 @@ mod runtime { pub type Coretime = coretime; #[runtime::pallet_index(67)] - pub type StakingAsyncAhClient = pallet_staking_async_ah_client; + pub type AssetHubStakingClient = pallet_staking_async_ah_client; // Migrations pallet #[runtime::pallet_index(98)] diff --git a/substrate/frame/staking-async/src/lib.rs b/substrate/frame/staking-async/src/lib.rs index bed5c6ebefcb9..4fc31810782af 100644 --- a/substrate/frame/staking-async/src/lib.rs +++ b/substrate/frame/staking-async/src/lib.rs @@ -80,13 +80,14 @@ use frame_support::{ BoundedVec, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, WeakBoundedVec, }; +use frame_system::pallet_prelude::BlockNumberFor; use ledger::LedgerIntegrityState; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, StaticLookup}, - BoundedBTreeMap, Perbill, RuntimeDebug, + traits::{AtLeast32BitUnsigned, One, StaticLookup, UniqueSaturatedInto}, + BoundedBTreeMap, Perbill, RuntimeDebug, Saturating, }; -use sp_staking::{EraIndex, ExposurePage, PagedExposureMetadata}; +use sp_staking::{EraIndex, ExposurePage, PagedExposureMetadata, SessionIndex}; pub use sp_staking::{Exposure, IndividualExposure, StakerStatus}; pub use weights::WeightInfo; @@ -422,3 +423,27 @@ impl Contains for AllStakers { Ledger::::contains_key(account) } } + +/// A smart type to determine the [`Config::PlanningEraOffset`], given: +/// +/// * Expected relay session duration, `RS` +/// * Time taking into consideration for XCM sending, `S` +/// +/// It will use the estimated election duration, the relay session duration, and add one as it knows +/// the relay chain will want to buffer validators for one session. This is needed because we use +/// this in our calculation based on the "active era". +pub struct PlanningEraOffsetOf(core::marker::PhantomData<(T, RS, S)>); +impl>, S: Get>> Get + for PlanningEraOffsetOf +{ + fn get() -> SessionIndex { + let election_duration = ::duration_with_export(); + let sessions_needed = (election_duration + S::get()) / RS::get(); + // add one, because we know the RC session pallet wants to buffer for one session, and + // another one cause we will receive activation report one session after that. + sessions_needed + .saturating_add(One::one()) + .saturating_add(One::one()) + .unique_saturated_into() + } +} diff --git a/substrate/frame/staking-async/src/mock.rs b/substrate/frame/staking-async/src/mock.rs index a146e9f068344..07f9c997eb061 100644 --- a/substrate/frame/staking-async/src/mock.rs +++ b/substrate/frame/staking-async/src/mock.rs @@ -34,7 +34,7 @@ use frame_support::{ }; use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot, EnsureSignedBy}; use pallet_staking_async_rc_client as rc_client; -use sp_core::ConstBool; +use sp_core::{ConstBool, ConstU64}; use sp_io; use sp_npos_elections::BalancingConfig; use sp_runtime::{traits::Zero, BuildStorage}; @@ -61,6 +61,22 @@ pub(crate) type AccountId = ::AccountId; pub(crate) type BlockNumber = BlockNumberFor; pub(crate) type Balance = ::Balance; +#[derive(Clone, Copy)] +pub enum PlanningEraMode { + Fixed(SessionIndex), + Smart, +} + +pub struct PlanningEraOffset; +impl Get for PlanningEraOffset { + fn get() -> SessionIndex { + match PlanningEraModeVal::get() { + PlanningEraMode::Fixed(value) => value, + PlanningEraMode::Smart => crate::PlanningEraOffsetOf::>::get(), + } + } +} + parameter_types! { pub static ExistentialDeposit: Balance = 1; pub static SlashDeferDuration: EraIndex = 0; @@ -73,7 +89,7 @@ parameter_types! { pub static MaxValidatorSet: u32 = 100; pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build(); pub static AbsoluteMaxNominations: u32 = 16; - pub static PlanningEraOffset: u32 = 1; + pub static PlanningEraModeVal: PlanningEraMode = PlanningEraMode::Fixed(2); // Session configs pub static SessionsPerEra: SessionIndex = 3; pub static Period: BlockNumber = 5; @@ -129,7 +145,8 @@ parameter_types! { pub static Pages: PageIndex = 1; pub static MaxBackersPerWinner: u32 = 256; pub static MaxWinnersPerPage: u32 = MaxValidatorSet::get(); - pub static StartReceived: bool = false; + pub static StartReceived: Option = None; + pub static ElectionDelay: BlockNumber = 0; } pub type InnerElection = onchain::OnChainExecution; @@ -164,22 +181,23 @@ impl ElectionProvider for TestElectionProvider { fn elect(page: PageIndex) -> Result, Self::Error> { if page == 0 { - StartReceived::set(false); + StartReceived::set(None); } InnerElection::elect(page) } fn start() -> Result<(), Self::Error> { - StartReceived::set(true); + StartReceived::set(Some(System::block_number())); Ok(()) } fn duration() -> Self::BlockNumber { - InnerElection::duration() + InnerElection::duration() + ElectionDelay::get() } fn status() -> Result { - if StartReceived::get() { - Ok(true) - } else { - Err(()) + let now = System::block_number(); + match StartReceived::get() { + Some(at) if now - at >= ElectionDelay::get() => Ok(true), + Some(_) => Ok(false), + None => Err(()), } } } @@ -336,7 +354,7 @@ pub mod session_mock { id: u32, prune_up_to: Option, ) { - log::debug!(target: "runtime::session_mock", "Received validator set: {:?}", new_validator_set); + log::debug!(target: "runtime::staking-async::session_mock", "Received validator set: {:?}", new_validator_set); let now = System::block_number(); // store the report for further inspection. ReceivedValidatorSets::mutate(|reports| { @@ -485,7 +503,15 @@ impl ExtBuilder { self } pub(crate) fn planning_era_offset(self, offset: SessionIndex) -> Self { - PlanningEraOffset::set(offset); + PlanningEraModeVal::set(PlanningEraMode::Fixed(offset)); + self + } + pub fn smart_era_planner(self) -> Self { + PlanningEraModeVal::set(PlanningEraMode::Smart); + self + } + pub fn election_delay(self, delay: BlockNumber) -> Self { + ElectionDelay::set(delay); self } pub(crate) fn nominate(mut self, nominate: bool) -> Self { diff --git a/substrate/frame/staking-async/src/pallet/impls.rs b/substrate/frame/staking-async/src/pallet/impls.rs index 83679103b5ad7..fc32d29a2f923 100644 --- a/substrate/frame/staking-async/src/pallet/impls.rs +++ b/substrate/frame/staking-async/src/pallet/impls.rs @@ -1057,7 +1057,7 @@ impl rc_client::AHStakingInterface for Pallet { /// implies a new validator set has been applied, and we must increment the active era to keep /// the systems in sync. fn on_relay_session_report(report: rc_client::SessionReport) { - log!(debug, "session report received\n{:?}", report,); + log!(debug, "session report received: {}", report,); let consumed_weight = T::WeightInfo::rc_on_session_report(); let rc_client::SessionReport { diff --git a/substrate/frame/staking-async/src/pallet/mod.rs b/substrate/frame/staking-async/src/pallet/mod.rs index 4e803b07442f1..23463628ee4d3 100644 --- a/substrate/frame/staking-async/src/pallet/mod.rs +++ b/substrate/frame/staking-async/src/pallet/mod.rs @@ -153,7 +153,7 @@ pub mod pallet { /// /// Following information is kept for eras in `[current_era - /// HistoryDepth, current_era]`: `ErasValidatorPrefs`, `ErasValidatorReward`, - /// `ErasRewardPoints`, `ErasTotalStake`, `ErasClaimedRewards`, + /// `ErasRewardPoints`, `ErasTotalStake`, `ClaimedRewards`, /// `ErasStakersPaged`, `ErasStakersOverview`. /// /// Must be more than the number of eras delayed by session. @@ -180,7 +180,7 @@ pub mod pallet { #[pallet::no_default_bounds] type Reward: OnUnbalanced>; - /// Number of sessions per era. + /// Number of sessions per era, as per the preferences of the **relay chain**. #[pallet::constant] type SessionsPerEra: Get; @@ -232,7 +232,7 @@ pub mod pallet { /// `MaxExposurePageSize` nominators. This is to limit the i/o cost for the /// nominator payout. /// - /// Note: `MaxExposurePageSize` is used to bound `ErasClaimedRewards` and is unsafe to + /// Note: `MaxExposurePageSize` is used to bound `ClaimedRewards` and is unsafe to /// reduce without handling it in a migration. #[pallet::constant] type MaxExposurePageSize: Get; @@ -609,8 +609,8 @@ pub mod pallet { OptionQuery, >; - pub struct ErasClaimedRewardsBound(core::marker::PhantomData); - impl Get for ErasClaimedRewardsBound { + pub struct ClaimedRewardsBound(core::marker::PhantomData); + impl Get for ClaimedRewardsBound { fn get() -> u32 { let max_total_nominators_per_validator = ::MaxBackersPerWinner::get(); @@ -628,13 +628,13 @@ pub mod pallet { /// /// It is removed after [`Config::HistoryDepth`] eras. #[pallet::storage] - pub type ErasClaimedRewards = StorageDoubleMap< + pub type ClaimedRewards = StorageDoubleMap< _, Twox64Concat, EraIndex, Twox64Concat, T::AccountId, - WeakBoundedVec>, + WeakBoundedVec>, ValueQuery, >; diff --git a/substrate/frame/staking-async/src/session_rotation.rs b/substrate/frame/staking-async/src/session_rotation.rs index 288375ef19190..2cec773065e08 100644 --- a/substrate/frame/staking-async/src/session_rotation.rs +++ b/substrate/frame/staking-async/src/session_rotation.rs @@ -93,7 +93,7 @@ use sp_staking::{ /// All of the following storage items must be controlled by this type: /// /// [`ErasValidatorPrefs`] -/// [`ErasClaimedRewards`] +/// [`ClaimedRewards`] /// [`ErasStakersPaged`] /// [`ErasStakersOverview`] /// [`ErasValidatorReward`] @@ -110,7 +110,7 @@ impl Eras { crate::log!(debug, "Pruning era {:?}", era); let mut cursor = >::clear_prefix(era, u32::MAX, None); debug_assert!(cursor.maybe_cursor.is_none()); - cursor = >::clear_prefix(era, u32::MAX, None); + cursor = >::clear_prefix(era, u32::MAX, None); debug_assert!(cursor.maybe_cursor.is_none()); cursor = >::clear_prefix((era,), u32::MAX, None); debug_assert!(cursor.maybe_cursor.is_none()); @@ -140,7 +140,7 @@ impl Eras { pub(crate) fn pending_rewards(era: EraIndex, validator: &T::AccountId) -> bool { >::get(&era, validator) .map(|overview| { - ErasClaimedRewards::::get(era, validator).len() < overview.page_count as usize + ClaimedRewards::::get(era, validator).len() < overview.page_count as usize }) .unwrap_or(false) } @@ -212,7 +212,7 @@ impl Eras { // Find next claimable page of paged exposure. let page_count = Self::exposure_page_count(era, validator); let all_claimable_pages: Vec = (0..page_count).collect(); - let claimed_pages = ErasClaimedRewards::::get(era, validator); + let claimed_pages = ClaimedRewards::::get(era, validator); all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p)) } @@ -220,7 +220,7 @@ impl Eras { /// Creates an entry to track validator reward has been claimed for a given era and page. /// Noop if already claimed. pub(crate) fn set_rewards_as_claimed(era: EraIndex, validator: &T::AccountId, page: Page) { - let mut claimed_pages = ErasClaimedRewards::::get(era, validator).into_inner(); + let mut claimed_pages = ClaimedRewards::::get(era, validator).into_inner(); // this should never be called if the reward has already been claimed if claimed_pages.contains(&page) { @@ -231,7 +231,7 @@ impl Eras { // add page to claimed entries claimed_pages.push(page); - ErasClaimedRewards::::insert( + ClaimedRewards::::insert( era, validator, WeakBoundedVec::<_, _>::force_from(claimed_pages, Some("set_rewards_as_claimed")), @@ -342,7 +342,7 @@ impl Eras { /// Check if the rewards for the given era and page index have been claimed. pub(crate) fn is_rewards_claimed(era: EraIndex, validator: &T::AccountId, page: Page) -> bool { - ErasClaimedRewards::::get(era, validator).contains(&page) + ClaimedRewards::::get(era, validator).contains(&page) } /// Add reward points to validators using their stash account ID. @@ -420,7 +420,7 @@ impl Eras { let e4 = ErasTotalStake::::contains_key(era); // these two are only populated conditionally, so we only check them for lack of existence - let e6 = ErasClaimedRewards::::iter_prefix_values(era).count() != 0; + let e6 = ClaimedRewards::::iter_prefix_values(era).count() != 0; let e7 = ErasRewardPoints::::contains_key(era); assert!( @@ -725,8 +725,7 @@ impl Rotator { let era_start_session = Self::active_era_start_session_index(); // progress of the active era in sessions. - let session_progress = - start_session.saturating_add(1).defensive_saturating_sub(era_start_session); + let session_progress = start_session.defensive_saturating_sub(era_start_session); log!( debug, diff --git a/substrate/frame/staking-async/src/tests/election_provider.rs b/substrate/frame/staking-async/src/tests/election_provider.rs index af9b9f8001837..a9b14b3ddbb51 100644 --- a/substrate/frame/staking-async/src/tests/election_provider.rs +++ b/substrate/frame/staking-async/src/tests/election_provider.rs @@ -24,7 +24,7 @@ use substrate_test_utils::assert_eq_uvec; use crate::tests::session_mock::ReceivedValidatorSets; #[test] -fn planning_era_offset_less_works() { +fn planning_era_offset_less_0() { // same as `basic_setup_sessions_per_era`, but notice how `PagedElectionProceeded` happens // one session later, and planning era is incremented one session later ExtBuilder::default() @@ -32,8 +32,62 @@ fn planning_era_offset_less_works() { .planning_era_offset(0) .no_flush_events() .build_and_execute(|| { - // this essentially makes the session duration 7, because the mock session will buffer - // for one session before activating the era. + // this essentially makes the session duration 8. After 6 sessions we realize we have do + // start an election (since offset = 0), then it is queued for one session (7), and then + // activated (8). + assert_eq!(Session::current_index(), 8); + assert_eq!(active_era(), 1); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 6, active_era: 0, planned_era: 1 }, + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 7, active_era: 0, planned_era: 1 }, + Event::EraPaid { era_index: 0, validator_payout: 20000, remainder: 20000 }, + Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 } + ] + ); + + Session::roll_until_active_era(2); + assert_eq!(Session::current_index(), 16); + assert_eq!(active_era(), 2); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 12, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 13, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 14, active_era: 1, planned_era: 2 }, + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 15, active_era: 1, planned_era: 2 }, + Event::EraPaid { era_index: 1, validator_payout: 20000, remainder: 20000 }, + Event::SessionRotated { starting_session: 16, active_era: 2, planned_era: 2 } + ] + ); + }); +} + +#[test] +fn planning_era_offset_works_1() { + // same as `basic_setup_sessions_per_era`, but notice how `PagedElectionProceeded` happens + // one session later, and planning era is incremented one session later + ExtBuilder::default() + .session_per_era(6) + .planning_era_offset(1) + .no_flush_events() + .build_and_execute(|| { + // this essentially makes the session duration 7. After 5 sessions we realize we have do + // start an election (since offset = 1), then it is queued for one session (6), and then + // activated (7). assert_eq!(Session::current_index(), 7); assert_eq!(active_era(), 1); @@ -74,14 +128,15 @@ fn planning_era_offset_less_works() { } #[test] -fn planning_era_offset_more_works() { +fn planning_era_offset_works_2() { ExtBuilder::default() .session_per_era(6) .planning_era_offset(2) .no_flush_events() .build_and_execute(|| { - // This effectively makes the era one session shorter. - assert_eq!(Session::current_index(), 5); + // start election at 4, and send it over. Buffered at 6, activated at 6. This is the + // expected behavior, and the default in `mock.rs`. + assert_eq!(Session::current_index(), 6); assert_eq!(active_era(), 1); assert_eq!( @@ -89,28 +144,123 @@ fn planning_era_offset_more_works() { vec![ Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 }, Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 }, - Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 1 }, + Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 }, Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 }, + Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 } + ] + ); + + Session::roll_until_active_era(2); + assert_eq!(Session::current_index(), 12); + assert_eq!(active_era(), 2); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 }, + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 }, + Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 } + ] + ); + }); +} + +#[test] +fn planning_era_offset_works_smart() { + ExtBuilder::default() + .session_per_era(6) + .smart_era_planner() + .no_flush_events() + .build_and_execute(|| { + // This works exactly the same as offset = 2, which means we send and rotate validators + // such that the era duration remains 6 sessions. + assert_eq!(Session::current_index(), 6); + assert_eq!(active_era(), 1); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 }, Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 }, - Event::EraPaid { era_index: 0, validator_payout: 12500, remainder: 12500 }, - Event::SessionRotated { starting_session: 5, active_era: 1, planned_era: 1 } + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 }, + Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 } ] ); Session::roll_until_active_era(2); - assert_eq!(Session::current_index(), 10); + assert_eq!(Session::current_index(), 12); assert_eq!(active_era(), 2); assert_eq!( staking_events_since_last_call(), vec![ - Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 }, Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 }, - Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 2 }, + Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 }, + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 }, + Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 } + ] + ); + }); +} + +#[test] +fn planning_era_offset_works_smart_with_delay() { + ExtBuilder::default() + .session_per_era(6) + .election_delay(7) + .smart_era_planner() + .no_flush_events() + .build_and_execute(|| { + // Same as above, but now election takes more time, more than 1 session to be exact. + // Notice how the era duration is kept at 6. + assert_eq!(Session::current_index(), 6); + assert_eq!(active_era(), 1); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 }, + Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 1 }, + Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 }, Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 }, + Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 } + ] + ); + + Session::roll_until_active_era(2); + assert_eq!(Session::current_index(), 12); + assert_eq!(active_era(), 2); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 }, + Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 }, Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 2 }, - Event::EraPaid { era_index: 1, validator_payout: 12500, remainder: 12500 }, - Event::SessionRotated { starting_session: 10, active_era: 2, planned_era: 2 } + Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 }, + Event::PagedElectionProceeded { page: 0, result: Ok(2) }, + Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 }, + Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 }, + Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 } ] ); }); @@ -426,7 +576,7 @@ mod paged_on_initialize_era_election_planner { // we will start the next election at the start of block 20 assert_eq!(System::block_number(), 15); - assert_eq!(PlanningEraOffset::get(), 1); + assert_eq!(PlanningEraOffset::get(), 2); // genesis validators are now in place. assert_eq!(current_era(), 1); @@ -541,7 +691,7 @@ mod paged_on_initialize_era_election_planner { // we will start the next election at the start of block 20 assert_eq!(System::block_number(), 15); - assert_eq!(PlanningEraOffset::get(), 1); + assert_eq!(PlanningEraOffset::get(), 2); // 1. election signal is sent here, Session::roll_until(20); diff --git a/substrate/frame/staking-async/src/tests/payout_stakers.rs b/substrate/frame/staking-async/src/tests/payout_stakers.rs index 3b7ab3c102643..0371d2db42936 100644 --- a/substrate/frame/staking-async/src/tests/payout_stakers.rs +++ b/substrate/frame/staking-async/src/tests/payout_stakers.rs @@ -317,7 +317,7 @@ fn reward_destination_staked() { let _ = staking_events_since_last_call(); mock::make_all_reward_payment(1); - assert_eq!(ErasClaimedRewards::::get(1, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(1, &11), vec![0]); assert_eq!( staking_events_since_last_call(), @@ -427,7 +427,7 @@ fn reward_destination_stash() { let _ = staking_events_since_last_call(); mock::make_all_reward_payment(1); - assert_eq!(ErasClaimedRewards::::get(1, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(1, &11), vec![0]); assert_eq!( staking_events_since_last_call(), @@ -475,7 +475,7 @@ fn reward_destination_account() { let _ = staking_events_since_last_call(); mock::make_all_reward_payment(1); - assert_eq!(ErasClaimedRewards::::get(1, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(1, &11), vec![0]); assert_eq!( staking_events_since_last_call(), @@ -984,7 +984,7 @@ fn test_multi_page_payout_stakers_by_page() { } } - assert_eq!(ErasClaimedRewards::::get(14, &11), vec![0, 1]); + assert_eq!(ClaimedRewards::::get(14, &11), vec![0, 1]); let last_era = 99; let history_depth = HistoryDepth::get(); @@ -999,7 +999,7 @@ fn test_multi_page_payout_stakers_by_page() { // verify we clean up history as we go for era in 0..15 { - assert!(ErasClaimedRewards::::get(era, &11).is_empty()); + assert!(ClaimedRewards::::get(era, &11).is_empty()); } // verify only page 0 is marked as claimed @@ -1009,7 +1009,7 @@ fn test_multi_page_payout_stakers_by_page() { first_claimable_reward_era, 0 )); - assert_eq!(ErasClaimedRewards::::get(first_claimable_reward_era, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(first_claimable_reward_era, &11), vec![0]); // verify page 0 and 1 are marked as claimed assert_ok!(Staking::payout_stakers_by_page( @@ -1018,7 +1018,7 @@ fn test_multi_page_payout_stakers_by_page() { first_claimable_reward_era, 1 )); - assert_eq!(ErasClaimedRewards::::get(first_claimable_reward_era, &11), vec![0, 1]); + assert_eq!(ClaimedRewards::::get(first_claimable_reward_era, &11), vec![0, 1]); // verify only page 0 is marked as claimed assert_ok!(Staking::payout_stakers_by_page( @@ -1027,7 +1027,7 @@ fn test_multi_page_payout_stakers_by_page() { last_reward_era, 0 )); - assert_eq!(ErasClaimedRewards::::get(last_reward_era, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(last_reward_era, &11), vec![0]); // verify page 0 and 1 are marked as claimed assert_ok!(Staking::payout_stakers_by_page( @@ -1036,15 +1036,15 @@ fn test_multi_page_payout_stakers_by_page() { last_reward_era, 1 )); - assert_eq!(ErasClaimedRewards::::get(last_reward_era, &11), vec![0, 1]); + assert_eq!(ClaimedRewards::::get(last_reward_era, &11), vec![0, 1]); // Out of order claims works. assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 69, 0)); - assert_eq!(ErasClaimedRewards::::get(69, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(69, &11), vec![0]); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 23, 1)); - assert_eq!(ErasClaimedRewards::::get(23, &11), vec![1]); + assert_eq!(ClaimedRewards::::get(23, &11), vec![1]); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 42, 0)); - assert_eq!(ErasClaimedRewards::::get(42, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(42, &11), vec![0]); }); } @@ -1166,7 +1166,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { } } - assert_eq!(ErasClaimedRewards::::get(14, &11), vec![0, 1]); + assert_eq!(ClaimedRewards::::get(14, &11), vec![0, 1]); let last_era = 99; let history_depth = HistoryDepth::get(); @@ -1181,7 +1181,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { // verify we clean up history as we go for era in 0..15 { - assert!(ErasClaimedRewards::::get(era, &11).is_empty()); + assert!(ClaimedRewards::::get(era, &11).is_empty()); } // verify only page 0 is marked as claimed @@ -1190,7 +1190,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { 11, first_claimable_reward_era )); - assert_eq!(ErasClaimedRewards::::get(first_claimable_reward_era, &11), vec![0]); + assert_eq!(ClaimedRewards::::get(first_claimable_reward_era, &11), vec![0]); // verify page 0 and 1 are marked as claimed assert_ok!(Staking::payout_stakers( @@ -1198,7 +1198,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { 11, first_claimable_reward_era, )); - assert_eq!(ErasClaimedRewards::::get(first_claimable_reward_era, &11), vec![0, 1]); + assert_eq!(ClaimedRewards::::get(first_claimable_reward_era, &11), vec![0, 1]); // change order and verify only page 1 is marked as claimed assert_ok!(Staking::payout_stakers_by_page( @@ -1207,12 +1207,12 @@ fn test_multi_page_payout_stakers_backward_compatible() { last_reward_era, 1 )); - assert_eq!(ErasClaimedRewards::::get(last_reward_era, &11), vec![1]); + assert_eq!(ClaimedRewards::::get(last_reward_era, &11), vec![1]); // verify page 0 is claimed even when explicit page is not passed assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, last_reward_era,)); - assert_eq!(ErasClaimedRewards::::get(last_reward_era, &11), vec![1, 0]); + assert_eq!(ClaimedRewards::::get(last_reward_era, &11), vec![1, 0]); // cannot claim any more pages assert_noop!( @@ -1237,10 +1237,10 @@ fn test_multi_page_payout_stakers_backward_compatible() { // Out of order claims works. assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, test_era, 2)); - assert_eq!(ErasClaimedRewards::::get(test_era, &11), vec![2]); + assert_eq!(ClaimedRewards::::get(test_era, &11), vec![2]); assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); - assert_eq!(ErasClaimedRewards::::get(test_era, &11), vec![2, 0]); + assert_eq!(ClaimedRewards::::get(test_era, &11), vec![2, 0]); // cannot claim page 2 again assert_noop!( @@ -1249,10 +1249,10 @@ fn test_multi_page_payout_stakers_backward_compatible() { ); assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); - assert_eq!(ErasClaimedRewards::::get(test_era, &11), vec![2, 0, 1]); + assert_eq!(ClaimedRewards::::get(test_era, &11), vec![2, 0, 1]); assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); - assert_eq!(ErasClaimedRewards::::get(test_era, &11), vec![2, 0, 1, 3]); + assert_eq!(ClaimedRewards::::get(test_era, &11), vec![2, 0, 1, 3]); }); } diff --git a/substrate/frame/staking-async/src/weights.rs b/substrate/frame/staking-async/src/weights.rs index 99bc91e56eab9..d318034d90591 100644 --- a/substrate/frame/staking-async/src/weights.rs +++ b/substrate/frame/staking-async/src/weights.rs @@ -34,7 +34,7 @@ // --pallet // pallet_staking_async // --extrinsic -// +// // --output // substrate/frame/staking-async/src/weights.rs // --steps @@ -506,8 +506,8 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Staking::ErasStakersOverview` (r:1 w:0) /// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) - /// Storage: `Staking::ErasClaimedRewards` (r:1 w:1) - /// Proof: `Staking::ErasClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Staking::ClaimedRewards` (r:1 w:1) + /// Proof: `Staking::ClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0) @@ -1276,8 +1276,8 @@ impl WeightInfo for () { } /// Storage: `Staking::ErasStakersOverview` (r:1 w:0) /// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) - /// Storage: `Staking::ErasClaimedRewards` (r:1 w:1) - /// Proof: `Staking::ErasClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Staking::ClaimedRewards` (r:1 w:1) + /// Proof: `Staking::ClaimedRewards` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0)