diff --git a/Cargo.lock b/Cargo.lock index 8eb4fa5b4e83..d479271777d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14079,6 +14079,7 @@ dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", + "polkadot-primitives-test-helpers", "scale-info", "serde", "sp-api", @@ -14092,6 +14093,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", + "sp-std 14.0.0", ] [[package]] diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index c796dc5f7c38..629fa728be37 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -137,7 +137,11 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidate_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidate_pending_availability(hash, para_id)? + .map(|receipt| receipt.into())) } async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult { @@ -260,7 +264,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { &self, relay_parent: PHash, ) -> RelayChainResult>> { - Ok(self.full_client.runtime_api().availability_cores(relay_parent)?) + Ok(self + .full_client + .runtime_api() + .availability_cores(relay_parent)? + .into_iter() + .map(|core_state| core_state.into()) + .collect::>()) } async fn candidates_pending_availability( @@ -268,7 +278,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidates_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidates_pending_availability(hash, para_id)? + .into_iter() + .map(|receipt| receipt.into()) + .collect::>()) } } diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index cdef39d5bdf1..1f2efdbbb5b3 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -21,12 +21,16 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - runtime_api, slashing, AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + runtime_api, slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, CandidateCommitments, + CandidateHash, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + InboundDownwardMessage, InboundHrmpMessage, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; use sp_consensus_grandpa::AuthorityId as GrandpaId; diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index e5e1e4d24ef9..7938223df23b 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -380,7 +380,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().availability_cores(at) + self.client + .runtime_api() + .availability_cores(at) + .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) } async fn persisted_validation_data( @@ -433,7 +436,10 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidate_pending_availability(at, para_id) + self.client + .runtime_api() + .candidate_pending_availability(at, para_id) + .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) } async fn candidates_pending_availability( @@ -441,11 +447,19 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidates_pending_availability(at, para_id) + self.client + .runtime_api() + .candidates_pending_availability(at, para_id) + .map(|candidates| { + candidates.into_iter().map(|candidate| candidate.into()).collect::>() + }) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client.runtime_api().candidate_events(at) + self.client + .runtime_api() + .candidate_events(at) + .map(|events| events.into_iter().map(|event| event.into()).collect::>()) } async fn dmq_contents( @@ -476,7 +490,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().on_chain_votes(at) + self.client + .runtime_api() + .on_chain_votes(at) + .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) } async fn session_executor_params( @@ -588,7 +605,12 @@ where at: Hash, para_id: Id, ) -> Result, ApiError> { - self.client.runtime_api().para_backing_state(at, para_id) + self.client + .runtime_api() + .para_backing_state(at, para_id) + .map(|maybe_backing_state| { + maybe_backing_state.map(|backing_state| backing_state.into()) + }) } async fn async_backing_params( diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs index 71bcdaffac4e..9375aca6ed73 100644 --- a/polkadot/node/test/client/src/block_builder.rs +++ b/polkadot/node/test/client/src/block_builder.rs @@ -16,7 +16,7 @@ use crate::Client; use codec::{Decode, Encode}; -use polkadot_primitives::{Block, InherentData as ParachainsInherentData}; +use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block}; use polkadot_test_runtime::UncheckedExtrinsic; use polkadot_test_service::GetLastTimestamp; use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 8f7ec314ecff..a8cd6cb5f4e0 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -28,10 +28,14 @@ sp-consensus-slots = { features = ["serde"], workspace = true } sp-io = { workspace = true } sp-keystore = { optional = true, workspace = true } sp-staking = { features = ["serde"], workspace = true } +sp-std = { workspace = true, optional = true } polkadot-core-primitives = { workspace = true } polkadot-parachain-primitives = { workspace = true } +[dev-dependencies] +polkadot-primitives-test-helpers = { workspace = true } + [features] default = ["std"] std = [ @@ -54,9 +58,11 @@ std = [ "sp-keystore?/std", "sp-runtime/std", "sp-staking/std", + "sp-std/std", ] runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", ] +test = [] diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index b4816ad15075..ddebe99e6214 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -114,11 +114,15 @@ //! separated from the stable primitives. use crate::{ - async_backing, slashing, ApprovalVotingParams, AsyncBackingParams, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + self, CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AsyncBackingParams, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use alloc::{ @@ -260,7 +264,7 @@ sp_api::decl_runtime_apis! { /// Returns the state of parachain backing for a given para. #[api_version(7)] - fn para_backing_state(_: ppp::Id) -> Option>; + fn para_backing_state(_: ppp::Id) -> Option>; /// Returns candidate's acceptance limitations for asynchronous backing for a relay parent. #[api_version(7)] diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index b6928e372064..a51ee0bd99bf 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . //! `V7` Primitives. - use alloc::{ vec, vec::{IntoIter, Vec}, @@ -1157,7 +1156,7 @@ pub enum OccupiedCoreAssumption { Free, } -/// An even concerning a candidate. +/// An event concerning a candidate. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] pub enum CandidateEvent { @@ -2130,7 +2129,7 @@ impl> Default for SchedulerParams } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use bitvec::bitvec; use sp_core::sr25519; diff --git a/polkadot/primitives/src/vstaging/async_backing.rs b/polkadot/primitives/src/vstaging/async_backing.rs new file mode 100644 index 000000000000..8706214b5a01 --- /dev/null +++ b/polkadot/primitives/src/vstaging/async_backing.rs @@ -0,0 +1,76 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use super::*; + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +/// A candidate pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct CandidatePendingAvailability { + /// The hash of the candidate. + pub candidate_hash: CandidateHash, + /// The candidate's descriptor. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate. + pub commitments: CandidateCommitments, + /// The candidate's relay parent's number. + pub relay_parent_number: N, + /// The maximum Proof-of-Validity size allowed, in bytes. + pub max_pov_size: u32, +} + +impl From> + for crate::v8::async_backing::CandidatePendingAvailability +{ + fn from(value: CandidatePendingAvailability) -> Self { + Self { + candidate_hash: value.candidate_hash, + descriptor: value.descriptor.into(), + commitments: value.commitments, + relay_parent_number: value.relay_parent_number, + max_pov_size: value.max_pov_size, + } + } +} + +/// The per-parachain state of the backing system, including +/// state-machine constraints and candidates pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct BackingState { + /// The state-machine constraints of the parachain. + pub constraints: Constraints, + /// The candidates pending availability. These should be ordered, i.e. they should form + /// a sub-chain, where the first candidate builds on top of the required parent of the + /// constraints and each subsequent builds on top of the previous head-data. + pub pending_availability: Vec>, +} + +impl From> for crate::v8::async_backing::BackingState { + fn from(value: BackingState) -> Self { + Self { + constraints: value.constraints, + pending_availability: value + .pending_availability + .into_iter() + .map(|candidate| candidate.into()) + .collect::>(), + } + } +} diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 1429b0c326ac..57cba85c10d9 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,5 +15,913 @@ // along with Polkadot. If not, see . //! Staging Primitives. +use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here +use super::{ + async_backing::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, + HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, +}; +use bitvec::prelude::*; +use sp_application_crypto::ByteArray; + +use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_runtime::traits::Header as HeaderT; +use sp_staking::SessionIndex; +/// Async backing primitives +pub mod async_backing; + +/// A type representing the version of the candidate descriptor and internal version number. +#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct InternalVersion(pub u8); + +/// A type representing the version of the candidate descriptor. +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub enum CandidateDescriptorVersion { + /// The old candidate descriptor version. + V1, + /// The new `CandidateDescriptorV2`. + V2, + /// An unknown version. + Unknown, +} + +/// A unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateDescriptorV2 { + /// The ID of the para this is a candidate for. + para_id: ParaId, + /// The hash of the relay-chain block this is executed in the context of. + relay_parent: H, + /// Version field. The raw value here is not exposed, instead it is used + /// to determine the `CandidateDescriptorVersion`, see `fn version()`. + /// For the current version this field is set to `0` and will be incremented + /// by next versions. + version: InternalVersion, + /// The core index where the candidate is backed. + core_index: u16, + /// The session index of the candidate relay parent. + session_index: SessionIndex, + /// Reserved bytes. + reserved1: [u8; 25], + /// The blake2-256 hash of the persisted validation data. This is extra data derived from + /// relay-chain state which may vary based on bitfields included before the candidate. + /// Thus it cannot be derived entirely from the relay-parent. + persisted_validation_data_hash: Hash, + /// The blake2-256 hash of the PoV. + pov_hash: Hash, + /// The root of a block's erasure encoding Merkle tree. + erasure_root: Hash, + /// Reserved bytes. + reserved2: [u8; 64], + /// Hash of the para header that is being generated by this candidate. + para_head: Hash, + /// The blake2-256 hash of the validation code bytes. + validation_code_hash: ValidationCodeHash, +} + +impl From> for CandidateDescriptor { + fn from(value: CandidateDescriptorV2) -> Self { + Self { + para_id: value.para_id, + relay_parent: value.relay_parent, + collator: value.rebuild_collator_field(), + persisted_validation_data_hash: value.persisted_validation_data_hash, + pov_hash: value.pov_hash, + erasure_root: value.erasure_root, + signature: value.rebuild_signature_field(), + para_head: value.para_head, + validation_code_hash: value.validation_code_hash, + } + } +} + +#[cfg(any(feature = "runtime-benchmarks", feature = "test"))] +impl From> for CandidateDescriptorV2 { + fn from(value: CandidateDescriptor) -> Self { + Decode::decode(&mut value.encode().as_slice()).unwrap() + } +} + +impl CandidateDescriptorV2 { + /// Constructor + pub fn new( + para_id: Id, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + erasure_root: Hash, + para_head: Hash, + validation_code_hash: ValidationCodeHash, + ) -> Self { + Self { + para_id, + relay_parent, + version: InternalVersion(0), + core_index: core_index.0 as u16, + session_index, + reserved1: [0; 25], + persisted_validation_data_hash, + pov_hash, + erasure_root, + reserved2: [0; 64], + para_head, + validation_code_hash, + } + } + + /// Set the PoV size in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_pov_hash(&mut self, pov_hash: Hash) { + self.pov_hash = pov_hash; + } + + /// Set the version in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_version(&mut self, version: InternalVersion) { + self.version = version; + } +} + +/// A candidate-receipt at version 2. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The hash of the encoded commitments made as a result of candidate execution. + pub commitments_hash: Hash, +} + +/// A candidate-receipt with commitments directly included. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CommittedCandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate receipt. + pub commitments: CandidateCommitments, +} + +/// An event concerning a candidate. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CandidateEvent { + /// This candidate receipt was backed in the most recent block. + /// This includes the core index the candidate is now occupying. + #[codec(index = 0)] + CandidateBacked(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was included and became a parablock at the most recent block. + /// This includes the core index the candidate was occupying as well as the group responsible + /// for backing the candidate. + #[codec(index = 1)] + CandidateIncluded(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was not made available in time and timed out. + /// This includes the core index the candidate was occupying. + #[codec(index = 2)] + CandidateTimedOut(CandidateReceiptV2, HeadData, CoreIndex), +} + +impl From> for super::v8::CandidateEvent { + fn from(value: CandidateEvent) -> Self { + match value { + CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateBacked( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateIncluded( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) => + super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), + } + } +} + +impl CandidateReceiptV2 { + /// Get a reference to the candidate descriptor. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.descriptor + } + + /// Computes the blake2-256 hash of the receipt. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + CandidateHash(BlakeTwo256::hash_of(self)) + } +} + +impl CommittedCandidateReceiptV2 { + /// Transforms this into a plain `CandidateReceipt`. + pub fn to_plain(&self) -> CandidateReceiptV2 { + CandidateReceiptV2 { + descriptor: self.descriptor.clone(), + commitments_hash: self.commitments.hash(), + } + } + + /// Computes the hash of the committed candidate receipt. + /// + /// This computes the canonical hash, not the hash of the directly encoded data. + /// Thus this is a shortcut for `candidate.to_plain().hash()`. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + self.to_plain().hash() + } + + /// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]? + pub fn corresponds_to(&self, receipt: &CandidateReceiptV2) -> bool + where + H: PartialEq, + { + receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash() + } +} + +impl PartialOrd for CommittedCandidateReceiptV2 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CommittedCandidateReceiptV2 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.descriptor + .para_id + .cmp(&other.descriptor.para_id) + .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data)) + } +} + +impl From> for super::v8::CommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments: value.commitments } + } +} + +impl From> for super::v8::CandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } + } +} + +/// A strictly increasing sequence number, typically this would be the least significant byte of the +/// block number. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct CoreSelector(pub u8); + +/// An offset in the relay chain claim queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct ClaimQueueOffset(pub u8); + +/// Signals that a parachain can send to the relay chain via the UMP queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum UMPSignal { + /// A message sent by a parachain to select the core the candidate is commited to. + /// Relay chain validators, in particular backers, use the `CoreSelector` and + /// `ClaimQueueOffset` to compute the index of the core the candidate has commited to. + SelectCore(CoreSelector, ClaimQueueOffset), +} +/// Separator between `XCM` and `UMPSignal`. +pub const UMP_SEPARATOR: Vec = vec![]; + +impl CandidateCommitments { + /// Returns the core selector and claim queue offset the candidate has committed to, if any. + pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { + // We need at least 2 messages for the separator and core selector + if self.upward_messages.len() < 2 { + return None + } + + let separator_pos = + self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; + + // Use first commitment + let message = self.upward_messages.get(separator_pos + 1)?; + + match UMPSignal::decode(&mut message.as_slice()).ok()? { + UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), + } + } +} + +/// CandidateReceipt construction errors. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum CandidateReceiptError { + /// The specified core index is invalid. + InvalidCoreIndex, + /// The core index in commitments doesn't match the one in descriptor + CoreIndexMismatch, + /// The core selector or claim queue offset is invalid. + InvalidSelectedCore, + /// The parachain is not assigned to any core at specified claim queue offset. + NoAssignment, + /// No core was selected. + NoCoreSelected, + /// Unknown version. + UnknownVersion(InternalVersion), +} + +macro_rules! impl_getter { + ($field:ident, $type:ident) => { + /// Returns the value of $field field. + pub fn $field(&self) -> $type { + self.$field + } + }; +} + +impl CandidateDescriptorV2 { + impl_getter!(erasure_root, Hash); + impl_getter!(para_head, Hash); + impl_getter!(relay_parent, H); + impl_getter!(para_id, ParaId); + impl_getter!(persisted_validation_data_hash, Hash); + impl_getter!(pov_hash, Hash); + impl_getter!(validation_code_hash, ValidationCodeHash); + + /// Returns the candidate descriptor version. + /// The candidate is at version 2 if the reserved fields are zeroed out + /// and the internal `version` field is 0. + pub fn version(&self) -> CandidateDescriptorVersion { + if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] { + return CandidateDescriptorVersion::V1 + } + + match self.version.0 { + 0 => CandidateDescriptorVersion::V2, + _ => CandidateDescriptorVersion::Unknown, + } + } + + fn rebuild_collator_field(&self) -> CollatorId { + let mut collator_id = Vec::with_capacity(32); + let core_index: [u8; 2] = self.core_index.to_ne_bytes(); + let session_index: [u8; 4] = self.session_index.to_ne_bytes(); + + collator_id.push(self.version.0); + collator_id.extend_from_slice(core_index.as_slice()); + collator_id.extend_from_slice(session_index.as_slice()); + collator_id.extend_from_slice(self.reserved1.as_slice()); + + CollatorId::from_slice(&collator_id.as_slice()) + .expect("Slice size is exactly 32 bytes; qed") + } + + /// Returns the collator id if this is a v1 `CandidateDescriptor` + pub fn collator(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + Some(self.rebuild_collator_field()) + } else { + None + } + } + + fn rebuild_signature_field(&self) -> CollatorSignature { + CollatorSignature::from_slice(self.reserved2.as_slice()) + .expect("Slice size is exactly 64 bytes; qed") + } + + /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. + pub fn signature(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return Some(self.rebuild_signature_field()) + } + + None + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn core_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(CoreIndex(self.core_index as u32)) + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn session_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(self.session_index) + } +} + +impl CommittedCandidateReceiptV2 { + /// Checks if descriptor core index is equal to the commited core index. + /// Input `assigned_cores` must contain the sorted cores assigned to the para at + /// the committed claim queue offset. + pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> { + // Don't check v1 descriptors. + if self.descriptor.version() == CandidateDescriptorVersion::V1 { + return Ok(()) + } + + if self.descriptor.version() == CandidateDescriptorVersion::Unknown { + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)) + } + + if assigned_cores.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + + let (core_selector, _cq_offset) = + self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; + + let core_index = assigned_cores + .get(core_selector.0 as usize % assigned_cores.len()) + .ok_or(CandidateReceiptError::InvalidCoreIndex)?; + + if *core_index != descriptor_core_index { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + Ok(()) + } +} + +/// A backed (or backable, depending on context) candidate. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BackedCandidate { + /// The candidate referred to. + candidate: CommittedCandidateReceiptV2, + /// The validity votes themselves, expressed as signatures. + validity_votes: Vec, + /// The indices of the validators within the group, expressed as a bitfield. May be extended + /// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is + /// enabled. + validator_indices: BitVec, +} + +/// Parachains inherent-data passed into the runtime by a block author +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct InherentData { + /// Signed bitfields by validators about availability. + pub bitfields: UncheckedSignedAvailabilityBitfields, + /// Backed candidates for inclusion in the block. + pub backed_candidates: Vec>, + /// Sets of dispute votes for inclusion, + pub disputes: MultiDisputeStatementSet, + /// The parent block header. Used for checking state proofs. + pub parent_header: HDR, +} + +impl BackedCandidate { + /// Constructor + pub fn new( + candidate: CommittedCandidateReceiptV2, + validity_votes: Vec, + validator_indices: BitVec, + core_index: Option, + ) -> Self { + let mut instance = Self { candidate, validity_votes, validator_indices }; + if let Some(core_index) = core_index { + instance.inject_core_index(core_index); + } + instance + } + + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceiptV2 { + &self.candidate + } + + /// Get a reference to the descriptor of the candidate. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.candidate.descriptor + } + + /// Get a mutable reference to the descriptor of the candidate. Only for testing. + #[cfg(feature = "test")] + pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2 { + &mut self.candidate.descriptor + } + + /// Get a reference to the validity votes of the candidate. + pub fn validity_votes(&self) -> &[ValidityAttestation] { + &self.validity_votes + } + + /// Get a mutable reference to validity votes of the para. + pub fn validity_votes_mut(&mut self) -> &mut Vec { + &mut self.validity_votes + } + + /// Compute this candidate's hash. + pub fn hash(&self) -> CandidateHash + where + H: Clone + Encode, + { + self.candidate.to_plain().hash() + } + + /// Get this candidate's receipt. + pub fn receipt(&self) -> CandidateReceiptV2 + where + H: Clone, + { + self.candidate.to_plain() + } + + /// Get a copy of the validator indices and the assumed core index, if any. + pub fn validator_indices_and_core_index( + &self, + core_index_enabled: bool, + ) -> (&BitSlice, Option) { + // This flag tells us if the block producers must enable Elastic Scaling MVP hack. + // It extends `BackedCandidate::validity_indices` to store a 8 bit core index. + if core_index_enabled { + let core_idx_offset = self.validator_indices.len().saturating_sub(8); + if core_idx_offset > 0 { + let (validator_indices_slice, core_idx_slice) = + self.validator_indices.split_at(core_idx_offset); + return ( + validator_indices_slice, + Some(CoreIndex(core_idx_slice.load::() as u32)), + ); + } + } + + (&self.validator_indices, None) + } + + /// Inject a core index in the validator_indices bitvec. + fn inject_core_index(&mut self, core_index: CoreIndex) { + let core_index_to_inject: BitVec = + BitVec::from_vec(vec![core_index.0 as u8]); + self.validator_indices.extend(core_index_to_inject); + } + + /// Update the validator indices and core index in the candidate. + pub fn set_validator_indices_and_core_index( + &mut self, + new_indices: BitVec, + maybe_core_index: Option, + ) { + self.validator_indices = new_indices; + + if let Some(core_index) = maybe_core_index { + self.inject_core_index(core_index); + } + } +} + +/// Scraped runtime backing votes and resolved disputes. +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct ScrapedOnChainVotes { + /// The session in which the block was included. + pub session: SessionIndex, + /// Set of backing validators for each candidate, represented by its candidate + /// receipt. + pub backing_validators_per_candidate: + Vec<(CandidateReceiptV2, Vec<(ValidatorIndex, ValidityAttestation)>)>, + /// On-chain-recorded set of disputes. + /// Note that the above `backing_validators` are + /// unrelated to the backers of the disputes candidates. + pub disputes: MultiDisputeStatementSet, +} + +impl From> for super::v8::ScrapedOnChainVotes { + fn from(value: ScrapedOnChainVotes) -> Self { + Self { + session: value.session, + backing_validators_per_candidate: value + .backing_validators_per_candidate + .into_iter() + .map(|(receipt, validators)| (receipt.into(), validators)) + .collect::>(), + disputes: value.disputes, + } + } +} + +/// Information about a core which is currently occupied. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct OccupiedCore { + // NOTE: this has no ParaId as it can be deduced from the candidate descriptor. + /// If this core is freed by availability, this is the assignment that is next up on this + /// core, if any. None if there is nothing queued for this core. + pub next_up_on_available: Option, + /// The relay-chain block number this began occupying the core at. + pub occupied_since: N, + /// The relay-chain block this will time-out at, if any. + pub time_out_at: N, + /// If this core is freed by being timed-out, this is the assignment that is next up on this + /// core. None if there is nothing queued for this core or there is no possibility of timing + /// out. + pub next_up_on_time_out: Option, + /// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding + /// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that + /// this will be available. + pub availability: BitVec, + /// The group assigned to distribute availability pieces of this candidate. + pub group_responsible: GroupIndex, + /// The hash of the candidate occupying the core. + pub candidate_hash: CandidateHash, + /// The descriptor of the candidate occupying the core. + pub candidate_descriptor: CandidateDescriptorV2, +} + +/// The state of a particular availability core. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CoreState { + /// The core is currently occupied. + #[codec(index = 0)] + Occupied(OccupiedCore), + /// The core is currently free, with a para scheduled and given the opportunity + /// to occupy. + /// + /// If a particular Collator is required to author this block, that is also present in this + /// variant. + #[codec(index = 1)] + Scheduled(ScheduledCore), + /// The core is currently free and there is nothing scheduled. This can be the case for + /// parathread cores when there are no parathread blocks queued. Parachain cores will never be + /// left idle. + #[codec(index = 2)] + Free, +} + +impl From> for super::v8::OccupiedCore { + fn from(value: OccupiedCore) -> Self { + Self { + next_up_on_available: value.next_up_on_available, + occupied_since: value.occupied_since, + time_out_at: value.time_out_at, + next_up_on_time_out: value.next_up_on_time_out, + availability: value.availability, + group_responsible: value.group_responsible, + candidate_hash: value.candidate_hash, + candidate_descriptor: value.candidate_descriptor.into(), + } + } +} + +impl From> for super::v8::CoreState { + fn from(value: CoreState) -> Self { + match value { + CoreState::Free => super::v8::CoreState::Free, + CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core), + CoreState::Occupied(occupied_core) => + super::v8::CoreState::Occupied(occupied_core.into()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + v8::{ + tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, + CommittedCandidateReceipt, Hash, HeadData, ValidationCode, + }, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2}, + }; + + fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") + } + + fn dummy_collator_id() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") + } + + pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { + let zeros = Hash::zero(); + let reserved2 = [0; 64]; + + CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2 { + para_id: 0.into(), + relay_parent: zeros, + version: InternalVersion(0), + core_index: 123, + session_index: 1, + reserved1: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved2, + para_head: zeros, + validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), + }, + commitments: CandidateCommitments { + head_data: HeadData(vec![]), + upward_messages: vec![].try_into().expect("empty vec fits within bounds"), + new_validation_code: None, + horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"), + processed_downward_messages: 0, + hrmp_watermark: 0_u32, + }, + } + } + + #[test] + fn is_binary_compatibile() { + let old_ccr = dummy_old_committed_candidate_receipt(); + let new_ccr = dummy_committed_candidate_receipt_v2(); + + assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); + + let encoded_old = old_ccr.encode(); + + // Deserialize from old candidate receipt. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_old.as_slice()).unwrap(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + } + + #[test] + fn invalid_version_descriptor() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2); + // Put some unknown version. + new_ccr.descriptor.version = InternalVersion(100); + + // Deserialize as V1. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut new_ccr.encode().as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); + assert_eq!( + new_ccr.check(&vec![].as_slice()), + Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) + ) + } + + #[test] + fn test_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + } + + #[test] + fn test_invalid_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 0; + new_ccr.descriptor.para_id = ParaId::new(1000); + + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // The check should fail because no `SelectCore` signal was sent. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + // Garbage message. + new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); + + // No `SelectCore` can be decoded. + assert_eq!(new_ccr.commitments.selected_core(), None); + + // Failure is expected. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + new_ccr.commitments.upward_messages.clear(); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + // Duplicate + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + // Duplicate doesn't override first signal. + assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(())); + } + + #[test] + fn test_version2_receipts_decoded_as_v1() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let encoded_ccr = new_ccr.encode(); + let decoded_ccr: CommittedCandidateReceipt = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent()); + assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id()); + + assert_eq!(new_ccr.hash(), decoded_ccr.hash()); + + // Encode v1 and decode as V2 + let encoded_ccr = new_ccr.encode(); + let v2_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + + assert_eq!(new_ccr.hash(), v2_ccr.hash()); + } + + #[test] + fn test_core_select_is_mandatory() { + // Testing edge case when collators provide zeroed signature and collator id. + let mut old_ccr = dummy_old_committed_candidate_receipt(); + old_ccr.descriptor.para_id = ParaId::new(1000); + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + // Since collator sig and id are zeroed, it means that the descriptor uses format + // version 2. + // We expect the check to fail in such case because there will be no `SelectCore` + // commitment. + assert_eq!(new_ccr.check(&vec![CoreIndex(0)]), Err(CandidateReceiptError::NoCoreSelected)); + + // Adding collator signature should make it decode as v1. + old_ccr.descriptor.signature = dummy_collator_signature(); + old_ccr.descriptor.collator = dummy_collator_id(); + + let old_ccr_hash = old_ccr.hash(); + + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature)); + assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator)); + + assert_eq!(new_ccr.descriptor.core_index(), None); + assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000)); + + assert_eq!(old_ccr_hash, new_ccr.hash()); + } +} diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index d43cf3317e57..b0f78717dd97 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,9 +23,10 @@ //! Note that `dummy_` prefixed values are meant to be fillers, that should not matter, and will //! contain randomness based data. use polkadot_primitives::{ + vstaging::{CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2}, CandidateCommitments, CandidateDescriptor, CandidateReceipt, CollatorId, CollatorSignature, - CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, PersistedValidationData, - ValidationCode, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, CoreIndex, Hash, HeadData, Id as ParaId, PersistedValidationData, + SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; pub use rand; use sp_application_crypto::sr25519; @@ -42,6 +43,14 @@ pub fn dummy_candidate_receipt>(relay_parent: H) -> CandidateRece } } +/// Creates a v2 candidate receipt with filler data. +pub fn dummy_candidate_receipt_v2>(relay_parent: H) -> CandidateReceiptV2 { + CandidateReceiptV2:: { + commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(), + descriptor: dummy_candidate_descriptor_v2(relay_parent), + } +} + /// Creates a committed candidate receipt with filler data. pub fn dummy_committed_candidate_receipt>( relay_parent: H, @@ -52,6 +61,16 @@ pub fn dummy_committed_candidate_receipt>( } } +/// Creates a v2 committed candidate receipt with filler data. +pub fn dummy_committed_candidate_receipt_v2>( + relay_parent: H, +) -> CommittedCandidateReceiptV2 { + CommittedCandidateReceiptV2 { + descriptor: dummy_candidate_descriptor_v2::(relay_parent), + commitments: dummy_candidate_commitments(dummy_head_data()), + } +} + /// Create a candidate receipt with a bogus signature and filler data. Optionally set the commitment /// hash with the `commitments` arg. pub fn dummy_candidate_receipt_bad_sig( @@ -124,6 +143,23 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD descriptor } +/// Create a v2 candidate descriptor with filler data. +pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { + let invalid = Hash::zero(); + let descriptor = make_valid_candidate_descriptor_v2( + 1.into(), + relay_parent, + CoreIndex(1), + 1, + invalid, + invalid, + invalid, + invalid, + invalid, + ); + descriptor +} + /// Create meaningless validation code. pub fn dummy_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -134,16 +170,16 @@ pub fn dummy_head_data() -> HeadData { HeadData(vec![]) } -/// Create a meaningless collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from(sr25519::Public::default()) -} - /// Create a meaningless validator id. pub fn dummy_validator() -> ValidatorId { ValidatorId::from(sr25519::Public::default()) } +/// Create a meaningless collator id. +pub fn dummy_collator() -> CollatorId { + CollatorId::from(sr25519::Public::default()) +} + /// Create a meaningless collator signature. pub fn dummy_collator_signature() -> CollatorSignature { CollatorSignature::from(sr25519::Signature::default()) @@ -232,6 +268,34 @@ pub fn make_valid_candidate_descriptor>( descriptor } +/// Create a v2 candidate descriptor. +pub fn make_valid_candidate_descriptor_v2>( + para_id: ParaId, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + validation_code_hash: impl Into, + para_head: Hash, + erasure_root: Hash, +) -> CandidateDescriptorV2 { + let validation_code_hash = validation_code_hash.into(); + + let descriptor = CandidateDescriptorV2::new( + para_id, + relay_parent, + core_index, + session_index, + persisted_validation_data_hash, + pov_hash, + erasure_root, + para_head, + validation_code_hash, + ); + + descriptor +} /// After manually modifying the candidate descriptor, resign with a defined collator key. pub fn resign_candidate_descriptor_with_collator>( descriptor: &mut CandidateDescriptor, diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index cfe373e8cba2..a3eec3f9d961 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -59,6 +59,8 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] +polkadot-primitives = { workspace = true, features = ["test"] } + futures = { workspace = true } hex-literal = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 65e56881c315..665737afa6cb 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,32 +30,38 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ - node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorV2, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + InherentData as ParachainsInherentData, + }, + AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, + GroupIndex, HeadData, Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; -use sp_core::{sr25519, ByteArray, H256}; +use sp_core::{ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; - -/// Create a null collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from_slice(&vec![0u8; 32]).expect("32 bytes; qed") +fn mock_validation_code() -> ValidationCode { + ValidationCode(vec![1, 2, 3]) } -/// Create a null collator signature. -pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") +// Create a dummy collator id suitable to be used in a V1 candidate descriptor. +fn junk_collator() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") } -fn mock_validation_code() -> ValidationCode { - ValidationCode(vec![1, 2, 3]) +// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. +fn junk_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") } /// Grab an account, seeded by a name and index. @@ -136,6 +142,8 @@ pub(crate) struct BenchBuilder { fill_claimqueue: bool, /// Cores which should not be available when being populated with pending candidates. unavailable_cores: Vec, + /// Use v2 candidate descriptor. + candidate_descriptor_v2: bool, _phantom: core::marker::PhantomData, } @@ -167,6 +175,7 @@ impl BenchBuilder { code_upgrade: None, fill_claimqueue: true, unavailable_cores: vec![], + candidate_descriptor_v2: false, _phantom: core::marker::PhantomData::, } } @@ -275,6 +284,12 @@ impl BenchBuilder { self } + /// Toggle usage of v2 candidate descriptors. + pub(crate) fn set_candidate_descriptor_v2(mut self, enable: bool) -> Self { + self.candidate_descriptor_v2 = enable; + self + } + /// Get the maximum number of validators per core. fn max_validators_per_core(&self) -> u32 { self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core()) @@ -310,18 +325,20 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptor { + fn candidate_descriptor_mock() -> CandidateDescriptorV2 { + // Use a v1 descriptor. CandidateDescriptor:: { para_id: 0.into(), relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), + collator: junk_collator(), persisted_validation_data_hash: Default::default(), pov_hash: Default::default(), erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), + signature: junk_collator_signature(), para_head: Default::default(), validation_code_hash: mock_validation_code().hash(), } + .into() } /// Create a mock of `CandidatePendingAvailability`. @@ -632,18 +649,35 @@ impl BenchBuilder { let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { + let descriptor = if self.candidate_descriptor_v2 { + CandidateDescriptorV2::new( + para_id, + relay_parent, + core_idx, + 1, + persisted_validation_data_hash, + pov_hash, + Default::default(), + head_data.hash(), + validation_code_hash, + ) + } else { + CandidateDescriptor:: { para_id, relay_parent, - collator: dummy_collator(), + collator: junk_collator(), persisted_validation_data_hash, pov_hash, erasure_root: Default::default(), - signature: dummy_collator_signature(), + signature: junk_collator_signature(), para_head: head_data.hash(), validation_code_hash, - }, + } + .into() + }; + + let candidate = CommittedCandidateReceipt:: { + descriptor, commitments: CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index 978ef718ea40..cb6329bf88ea 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -25,10 +25,9 @@ use bitvec::{bitvec, prelude::Lsb0}; use frame_benchmarking::benchmarks; use pallet_message_queue as mq; use polkadot_primitives::{ - CandidateCommitments, CollatorId, CollatorSignature, CommittedCandidateReceipt, HrmpChannelId, - OutboundHrmpMessage, SessionIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateCommitments, + HrmpChannelId, OutboundHrmpMessage, SessionIndex, }; -use sp_core::sr25519; fn create_candidate_commitments( para_id: ParaId, @@ -124,17 +123,17 @@ benchmarks! { let core_index = CoreIndex::from(0); let backing_group = GroupIndex::from(0); - let descriptor = CandidateDescriptor:: { - para_id: para, - relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), - persisted_validation_data_hash: Default::default(), - pov_hash: Default::default(), - erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), - para_head: Default::default(), - validation_code_hash: ValidationCode(vec![1, 2, 3]).hash(), - }; + let descriptor = CandidateDescriptor::::new( + para, + Default::default(), + CoreIndex(0), + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ValidationCode(vec![1, 2, 3]).hash(), + ); let receipt = CommittedCandidateReceipt:: { descriptor, diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 36a810d341c6..2a215d5d595c 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -20,8 +20,8 @@ pub mod v0 { use frame_support::{storage_alias, Twox64Concat}; use frame_system::pallet_prelude::BlockNumberFor; use polkadot_primitives::{ - AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CoreIndex, - GroupIndex, Id as ParaId, ValidatorIndex, + vstaging::CandidateDescriptorV2 as CandidateDescriptor, AvailabilityBitfield, + CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex, }; use scale_info::TypeInfo; @@ -219,7 +219,7 @@ mod tests { use frame_support::traits::UncheckedOnRuntimeUpgrade; use polkadot_primitives::{AvailabilityBitfield, Id as ParaId}; use polkadot_primitives_test_helpers::{ - dummy_candidate_commitments, dummy_candidate_descriptor, dummy_hash, + dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash, }; #[test] @@ -235,7 +235,7 @@ mod tests { let mut expected = vec![]; for i in 1..5 { - let descriptor = dummy_candidate_descriptor(dummy_hash()); + let descriptor = dummy_candidate_descriptor_v2(dummy_hash()); v0::PendingAvailability::::insert( ParaId::from(i), v0::CandidatePendingAvailability { @@ -285,7 +285,7 @@ mod tests { ParaId::from(6), v0::CandidatePendingAvailability { core: CoreIndex(6), - descriptor: dummy_candidate_descriptor(dummy_hash()), + descriptor: dummy_candidate_descriptor_v2(dummy_hash()), relay_parent_number: 6, hash: CandidateHash(dummy_hash()), availability_votes: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index fbf13339dfca..3fc1ddf0bf1a 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -45,11 +45,15 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_message_queue::OnQueueChanged; use polkadot_primitives::{ - effective_minimum_backing_votes, supermajority_threshold, well_known_keys, BackedCandidate, - CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateReceipt, - CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, - SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, - ValidityAttestation, + effective_minimum_backing_votes, supermajority_threshold, + vstaging::{ + BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + well_known_keys, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Hash, HeadData, + Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, + ValidatorIndex, ValidityAttestation, }; use scale_info::TypeInfo; use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; @@ -766,7 +770,7 @@ impl Pallet { let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; let signing_context = SigningContext { - parent_hash: backed_candidate.descriptor().relay_parent, + parent_hash: backed_candidate.descriptor().relay_parent(), session_index: shared::CurrentSessionIndex::::get(), }; @@ -885,7 +889,7 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); paras::Pallet::::schedule_code_upgrade( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), new_code, now, &config, @@ -895,19 +899,19 @@ impl Pallet { // enact the messaging facet of the candidate. dmp::Pallet::::prune_dmq( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.processed_downward_messages, ); Self::receive_upward_messages( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.upward_messages.as_slice(), ); hrmp::Pallet::::prune_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), BlockNumberFor::::from(commitments.hrmp_watermark), ); hrmp::Pallet::::queue_outbound_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.horizontal_messages, ); @@ -919,7 +923,7 @@ impl Pallet { )); paras::Pallet::::note_new_head( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.head_data, relay_parent_number, ); @@ -1255,8 +1259,8 @@ impl CandidateCheckContext { backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, parent_head_data: HeadData, ) -> Result, Error> { - let para_id = backed_candidate_receipt.descriptor().para_id; - let relay_parent = backed_candidate_receipt.descriptor().relay_parent; + let para_id = backed_candidate_receipt.descriptor.para_id(); + let relay_parent = backed_candidate_receipt.descriptor.relay_parent(); // Check that the relay-parent is one of the allowed relay-parents. let (relay_parent_storage_root, relay_parent_number) = { @@ -1276,7 +1280,7 @@ impl CandidateCheckContext { let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor.persisted_validation_data_hash(), Error::::ValidationDataHashMismatch, ); } @@ -1285,12 +1289,12 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor.validation_code_hash() == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.descriptor.para_head() == backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 95fd66bf8e4f..59114e28be16 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,22 +26,21 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, - UncheckedSignedAvailabilityBitfields, + effective_minimum_backing_votes, AvailabilityBitfield, CandidateDescriptor, + SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, + BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; -use polkadot_primitives_test_helpers::{ - dummy_collator, dummy_collator_signature, dummy_validation_code, -}; +use polkadot_primitives_test_helpers::dummy_validation_code; use sc_keystore::LocalKeystore; +use sp_core::ByteArray; use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -96,24 +95,6 @@ pub(crate) enum BackingKind { Lacking, } -pub(crate) fn collator_sign_candidate( - collator: Sr25519Keyring, - candidate: &mut CommittedCandidateReceipt, -) { - candidate.descriptor.collator = collator.public().into(); - - let payload = polkadot_primitives::collator_signature_payload( - &candidate.descriptor.relay_parent, - &candidate.descriptor.para_id, - &candidate.descriptor.persisted_validation_data_hash, - &candidate.descriptor.pov_hash, - &candidate.descriptor.validation_code_hash, - ); - - candidate.descriptor.signature = collator.sign(&payload[..]).into(); - assert!(candidate.descriptor().check_collator_signature().is_ok()); -} - pub(crate) fn back_candidate( candidate: CommittedCandidateReceipt, validators: &[Sr25519Keyring], @@ -311,9 +292,16 @@ impl TestCandidateBuilder { validation_code_hash: self.validation_code.hash(), para_head: self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), erasure_root: Default::default(), - signature: dummy_collator_signature(), - collator: dummy_collator(), - }, + signature: CollatorSignature::from_slice( + &mut (0..64).into_iter().collect::>().as_slice(), + ) + .expect("64 bytes; qed"), + collator: CollatorId::from_slice( + &mut (0..32).into_iter().collect::>().as_slice(), + ) + .expect("32 bytes; qed"), + } + .into(), commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, @@ -1244,7 +1232,7 @@ fn candidate_checks() { // Check candidate ordering { - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1253,7 +1241,7 @@ fn candidate_checks() { ..Default::default() } .build(); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1265,7 +1253,7 @@ fn candidate_checks() { .build(); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1281,10 +1269,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_1); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_2); - let backed_a = back_candidate( candidate_a, &validators, @@ -1356,7 +1340,7 @@ fn candidate_checks() { // candidate does not build on top of the latest unincluded head - let mut candidate_b_3 = TestCandidateBuilder { + let candidate_b_3 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(4), @@ -1371,7 +1355,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_3); let backed_b_3 = back_candidate( candidate_b_3, @@ -1396,7 +1379,7 @@ fn candidate_checks() { // candidate not backed. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1405,7 +1388,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); // Insufficient backing. let backed = back_candidate( @@ -1459,7 +1441,7 @@ fn candidate_checks() { let wrong_parent_hash = Hash::repeat_byte(222); assert!(System::parent_hash() != wrong_parent_hash); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: wrong_parent_hash, pov_hash: Hash::repeat_byte(1), @@ -1468,7 +1450,7 @@ fn candidate_checks() { } .build(); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1478,10 +1460,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b); - let backed_a = back_candidate( candidate_a, &validators, @@ -1531,10 +1509,9 @@ fn candidate_checks() { .build(); assert_eq!(CollatorId::from(Sr25519Keyring::Two.public()), thread_collator); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate); // change the candidate after signing. - candidate.descriptor.pov_hash = Hash::repeat_byte(2); + candidate.descriptor.set_pov_hash(Hash::repeat_byte(2)); let backed = back_candidate( candidate, @@ -1597,7 +1574,7 @@ fn candidate_checks() { // interfering code upgrade - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1608,8 +1585,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1648,7 +1623,7 @@ fn candidate_checks() { // Bad validation data hash - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1658,8 +1633,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1685,7 +1658,7 @@ fn candidate_checks() { // bad validation code hash { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1696,8 +1669,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1723,7 +1694,7 @@ fn candidate_checks() { // Para head hash in descriptor doesn't match head data { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1734,8 +1705,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1826,7 +1795,7 @@ fn backing_works() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1835,9 +1804,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1846,9 +1814,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1857,7 +1824,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let backed_a = back_candidate( candidate_a.clone(), @@ -1978,7 +1944,7 @@ fn backing_works() { Vec<(ValidatorIndex, ValidityAttestation)>, )>| { candidate_receipts_with_backers.sort_by(|(cr1, _), (cr2, _)| { - cr1.descriptor().para_id.cmp(&cr2.descriptor().para_id) + cr1.descriptor().para_id().cmp(&cr2.descriptor().para_id()) }); candidate_receipts_with_backers }; @@ -2121,7 +2087,7 @@ fn backing_works_with_elastic_scaling_mvp() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2130,9 +2096,8 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -2141,10 +2106,9 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -2158,7 +2122,6 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); let backed_a = back_candidate( candidate_a.clone(), @@ -2396,7 +2359,7 @@ fn can_include_candidate_with_ok_code_upgrade() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2406,7 +2369,6 @@ fn can_include_candidate_with_ok_code_upgrade() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), @@ -2556,7 +2518,7 @@ fn check_allowed_relay_parents() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: relay_parent_a.1, pov_hash: Hash::repeat_byte(1), @@ -2569,10 +2531,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let signing_context_a = SigningContext { parent_hash: relay_parent_a.1, session_index: 5 }; - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: relay_parent_b.1, pov_hash: Hash::repeat_byte(2), @@ -2585,10 +2546,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); let signing_context_b = SigningContext { parent_hash: relay_parent_b.1, session_index: 5 }; - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: relay_parent_c.1, pov_hash: Hash::repeat_byte(3), @@ -2601,7 +2561,6 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let signing_context_c = SigningContext { parent_hash: relay_parent_c.1, session_index: 5 }; let backed_a = back_candidate( @@ -2823,7 +2782,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2833,7 +2792,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 1742e91276d4..fa466de11987 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -144,8 +144,8 @@ benchmarks! { // Traverse candidates and assert descriptors are as expected for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!(backing_validators.1.len(), votes); } @@ -203,8 +203,8 @@ benchmarks! { for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!( backing_validators.1.len(), votes, diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index bd8d08a842c3..84d8299cd29c 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -48,12 +48,17 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use polkadot_primitives::{ - effective_minimum_backing_votes, node_features::FeatureIndex, BackedCandidate, CandidateHash, - CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, - DisputeStatementSet, HeadData, InherentData as ParachainsInherentData, - MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, - SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorVersion, CandidateReceiptV2 as CandidateReceipt, + InherentData as ParachainsInherentData, ScrapedOnChainVotes, + }, + CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, + DisputeStatementSet, HeadData, MultiDisputeStatementSet, SessionIndex, + SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, + PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; use scale_info::TypeInfo; @@ -594,6 +599,12 @@ impl Pallet { .map(|b| *b) .unwrap_or(false); + let allow_v2_receipts = configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + let mut eligible: BTreeMap> = BTreeMap::new(); let mut total_eligible_cores = 0; @@ -610,6 +621,7 @@ impl Pallet { concluded_invalid_hashes, eligible, core_index_enabled, + allow_v2_receipts, ); let count = count_backed_candidates(&backed_candidates_with_core); @@ -787,7 +799,7 @@ pub(crate) fn apply_weight_limit( let mut current_para_id = None; for candidate in core::mem::take(candidates).into_iter() { - let candidate_para_id = candidate.descriptor().para_id; + let candidate_para_id = candidate.descriptor().para_id(); if Some(candidate_para_id) == current_para_id { let chain = chained_candidates .last_mut() @@ -966,14 +978,15 @@ pub(crate) fn sanitize_bitfields( /// subsequent candidates after the filtered one. /// /// Filter out: -/// 1. any candidates which don't form a chain with the other candidates of the paraid (even if they +/// 1. Candidates that have v2 descriptors if the node `CandidateReceiptV2` feature is not enabled. +/// 2. any candidates which don't form a chain with the other candidates of the paraid (even if they /// do form a chain but are not in the right order). -/// 2. any candidates that have a concluded invalid dispute or who are descendants of a concluded +/// 3. any candidates that have a concluded invalid dispute or who are descendants of a concluded /// invalid candidate. -/// 3. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// 4. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned /// but have no injected core index. -/// 4. all backing votes from disabled validators -/// 5. any candidates that end up with less than `effective_minimum_backing_votes` backing votes +/// 5. all backing votes from disabled validators +/// 6. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// /// Returns the scheduled /// backed candidates which passed filtering, mapped by para id and in the right dependency order. @@ -983,13 +996,28 @@ fn sanitize_backed_candidates( concluded_invalid_with_descendants: BTreeSet, scheduled: BTreeMap>, core_index_enabled: bool, + allow_v2_receipts: bool, ) -> BTreeMap, CoreIndex)>> { // Map the candidates to the right paraids, while making sure that the order between candidates // of the same para is preserved. let mut candidates_per_para: BTreeMap> = BTreeMap::new(); for candidate in backed_candidates { + // Drop any v2 candidate receipts if nodes are not allowed to use them. + // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure + // any v1 descendants of v2 candidates are dropped. + if !allow_v2_receipts && candidate.descriptor().version() == CandidateDescriptorVersion::V2 + { + log::debug!( + target: LOG_TARGET, + "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id() + ); + continue + } + candidates_per_para - .entry(candidate.descriptor().para_id) + .entry(candidate.descriptor().para_id()) .or_default() .push(candidate); } @@ -1008,7 +1036,7 @@ fn sanitize_backed_candidates( target: LOG_TARGET, "Found backed candidate {:?} which was concluded invalid or is a descendant of a concluded invalid candidate, for paraid {:?}.", candidate.candidate().hash(), - candidate.descriptor().para_id + candidate.descriptor().para_id() ); } keep @@ -1189,13 +1217,13 @@ fn filter_backed_statements_from_disabled_validators< // Get relay parent block number of the candidate. We need this to get the group index // assigned to this core at this block number let relay_parent_block_number = - match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent, None) { + match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent(), None) { Some((_, block_num)) => block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", - bc.descriptor().relay_parent + bc.descriptor().relay_parent() ); return false }, @@ -1396,7 +1424,7 @@ fn map_candidates_to_cores block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate.candidate().hash(), ); return None diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index e34055bfa9f2..ac42ac1611df 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -58,7 +58,9 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned}; + use polkadot_primitives::{ + vstaging::InternalVersion, AvailabilityBitfield, SchedulerParams, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -70,6 +72,7 @@ mod enter { fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, + v2_descriptor: bool, } fn make_inherent_data( @@ -82,6 +85,7 @@ mod enter { fill_claimqueue, elastic_paras, unavailable_cores, + v2_descriptor, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -99,7 +103,8 @@ mod enter { .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) .set_fill_claimqueue(fill_claimqueue) - .set_unavailable_cores(unavailable_cores); + .set_unavailable_cores(unavailable_cores) + .set_candidate_descriptor_v2(v2_descriptor); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); @@ -145,6 +150,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -240,6 +246,7 @@ mod enter { fill_claimqueue: false, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -344,6 +351,7 @@ mod enter { fill_claimqueue: true, elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), + v2_descriptor: false, }); let mut expected_para_inherent_data = scenario.data.clone(); @@ -600,6 +608,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -673,6 +682,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -744,6 +754,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -831,6 +842,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -918,6 +930,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -977,6 +990,7 @@ mod enter { fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1063,6 +1077,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1170,6 +1185,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1238,6 +1254,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1304,6 +1321,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1407,6 +1425,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let mut para_inherent_data = scenario.data.clone(); @@ -1440,7 +1459,7 @@ mod enter { // The chained candidates are not picked, instead a single other candidate is picked assert_eq!(backed_candidates.len(), 1); - assert_ne!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + assert_ne!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1461,9 +1480,9 @@ mod enter { // Only the chained candidates should pass filter. assert_eq!(backed_candidates.len(), 3); // Check the actual candidates - assert_eq!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[1].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[2].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[1].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[2].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1496,6 +1515,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1524,6 +1544,76 @@ mod enter { assert_eq!(dispatch_error, Error::::InherentOverweight.into()); }); } + + #[test] + fn v2_descriptors_are_filtered() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + // 8 cores ! + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + }); + + let mut unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + // Make the last candidate look like v1, by using an unknown version. + unfiltered_para_inherent_data.backed_candidates[9] + .descriptor_mut() + .set_version(InternalVersion(123)); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + // We expect all backed candidates to be filtered out. + let filtered_para_inherend_data = + Pallet::::create_inherent_inner(&inherent_data).unwrap(); + + assert_eq!(filtered_para_inherend_data.backed_candidates.len(), 0); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } } fn default_header() -> polkadot_primitives::Header { @@ -1540,9 +1630,7 @@ mod sanitizers { use super::*; use crate::{ - inclusion::tests::{ - back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder, - }, + inclusion::tests::{back_candidate, BackingKind, TestCandidateBuilder}, mock::new_test_ext, }; use bitvec::order::Lsb0; @@ -1556,7 +1644,6 @@ mod sanitizers { use crate::mock::Test; use polkadot_primitives::PARACHAIN_KEY_TYPE_ID; use sc_keystore::LocalKeystore; - use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -1940,7 +2027,7 @@ mod sanitizers { .into_iter() .map(|idx0| { let idx1 = idx0 + 1; - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(idx1), relay_parent, pov_hash: Hash::repeat_byte(idx1 as u8), @@ -1957,8 +2044,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1991,7 +2076,7 @@ mod sanitizers { let mut expected_backed_candidates_with_core = BTreeMap::new(); for candidate in backed_candidates.iter() { - let para_id = candidate.descriptor().para_id; + let para_id = candidate.descriptor().para_id(); expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push(( candidate.clone(), @@ -2177,7 +2262,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2195,8 +2280,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2215,7 +2298,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2233,8 +2316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2255,7 +2336,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2272,8 +2353,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2294,7 +2373,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2311,8 +2390,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2331,7 +2408,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2348,8 +2425,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -2366,7 +2441,7 @@ mod sanitizers { .or_insert(vec![]) .push((backed, CoreIndex(5))); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2384,8 +2459,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2402,7 +2475,7 @@ mod sanitizers { // Para 6. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(6), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2419,8 +2492,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2435,7 +2506,7 @@ mod sanitizers { // Para 7. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(7), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2452,8 +2523,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2468,7 +2537,7 @@ mod sanitizers { // Para 8. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(8), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2485,8 +2554,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2707,7 +2774,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2725,8 +2792,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let prev_backed: BackedCandidate = back_candidate( candidate, @@ -2738,7 +2803,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2756,8 +2821,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2773,7 +2836,7 @@ mod sanitizers { // Para 2. { - let mut candidate_1 = TestCandidateBuilder { + let candidate_1 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2791,8 +2854,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); - let backed_1: BackedCandidate = back_candidate( candidate_1, &validators, @@ -2811,7 +2872,7 @@ mod sanitizers { .push((backed_1, CoreIndex(2))); } - let mut candidate_2 = TestCandidateBuilder { + let candidate_2 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2829,8 +2890,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); - let backed_2 = back_candidate( candidate_2.clone(), &validators, @@ -2842,7 +2901,7 @@ mod sanitizers { ); backed_candidates.push(backed_2.clone()); - let mut candidate_3 = TestCandidateBuilder { + let candidate_3 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2860,8 +2919,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); - let backed_3 = back_candidate( candidate_3, &validators, @@ -2876,7 +2933,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2894,8 +2951,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2914,7 +2969,7 @@ mod sanitizers { .push((backed, CoreIndex(5))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2932,8 +2987,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2954,7 +3007,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(8 as u8), @@ -2972,8 +3025,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed: BackedCandidate = back_candidate( candidate.clone(), &validators, @@ -3210,7 +3261,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3228,8 +3279,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3248,7 +3297,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3267,8 +3316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3281,7 +3328,7 @@ mod sanitizers { ); backed_candidates.push(backed.clone()); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3300,8 +3347,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3316,7 +3361,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3334,8 +3379,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3354,7 +3397,7 @@ mod sanitizers { .push((backed, CoreIndex(3))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3373,8 +3416,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3393,7 +3434,7 @@ mod sanitizers { .push((backed, CoreIndex(4))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3412,8 +3453,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3486,7 +3525,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3510,7 +3550,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3535,6 +3576,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3567,6 +3609,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3607,6 +3650,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3677,6 +3721,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3715,6 +3760,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); assert!(sanitized_backed_candidates.is_empty()); @@ -3751,6 +3797,7 @@ mod sanitizers { set, scheduled, core_index_enabled, + false, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); @@ -3773,9 +3820,9 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 0 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 0 { invalid_set.insert(backed_candidate.hash()); - } else if backed_candidate.descriptor().para_id == ParaId::from(3) { + } else if backed_candidate.descriptor().para_id() == ParaId::from(3) { invalid_set.insert(backed_candidate.hash()); } } @@ -3788,6 +3835,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // We'll be left with candidates from paraid 2 and 4. @@ -3811,7 +3859,7 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 1 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 1 { invalid_set.insert(backed_candidate.hash()); } } @@ -3824,6 +3872,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // Only the second candidate of paraid 1 should be removed. diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 697890232211..ead825b38f07 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -27,15 +27,19 @@ use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use polkadot_primitives::{ async_backing::{ - AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints, - InboundHrmpLimitations, OutboundHrmpChannelLimitations, + AsyncBackingParams, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, }, - slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - NodeFeatures, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + async_backing::{BackingState, CandidatePendingAvailability}, + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + OccupiedCore, ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sp_runtime::traits::One; diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 4aa381e33b1b..a3440f686e94 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -21,7 +21,9 @@ use alloc::{ collections::{btree_map::BTreeMap, vec_deque::VecDeque}, vec::Vec, }; -use polkadot_primitives::{CommittedCandidateReceipt, CoreIndex, Id as ParaId}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, Id as ParaId, +}; use sp_runtime::traits::One; /// Returns the claimqueue from the scheduler diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index dfc41b15bb1d..6b046e190830 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -35,12 +35,16 @@ use frame_support::{ }; use pallet_nis::WithMaximumOf; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, - SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, SessionInfo, Signature, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, claims, crowdloan, identity_migrator, impl_runtime_weights, @@ -2032,7 +2036,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8e34320d38f2..72d024e9a878 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -59,10 +59,14 @@ use pallet_session::historical as session_historical; use pallet_timestamp::Now; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, Balance, BlockNumber, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -978,7 +982,7 @@ sp_api::impl_runtime_apis! { runtime_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { runtime_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index e8fe11615d74..b02c2d8c671e 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -49,12 +49,16 @@ use pallet_identity::legacy::IdentityInfo; use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, FungibleAdapter, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionInfo, Signature, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, crowdloan, @@ -2064,7 +2068,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc new file mode 100644 index 000000000000..b4cf261f33a4 --- /dev/null +++ b/prdoc/pr_5322.prdoc @@ -0,0 +1,30 @@ +title: Elastic scaling - introduce new candidate receipt primitive + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Introduces `CandidateDescriptorV2` primitive as described in [RFC 103](https://github.com/polkadot-fellows/RFCs/pull/103). + Updates parachains runtime, Westend, Rococo and test runtimes to use the new primitives. + This change does not implement the functionality of the new candidate receipts. + +crates: +- name: polkadot-primitives + bump: minor +- name: polkadot-primitives-test-helpers + bump: minor +- name: polkadot-runtime-parachains + bump: major +- name: rococo-runtime + bump: major +- name: westend-runtime + bump: major +- name: polkadot-test-runtime + bump: major +- name: polkadot-service + bump: patch +- name: polkadot-node-subsystem-types + bump: patch +- name: polkadot-test-client + bump: major +- name: cumulus-relay-chain-inprocess-interface + bump: patch