From 793142e5c2e97253fe21e1e80217b08e077857bd Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 17 Jul 2024 15:19:14 +0300 Subject: [PATCH 01/40] WIP primitives Signed-off-by: Andrei Sandu --- polkadot/primitives/src/v7/mod.rs | 2 +- polkadot/primitives/src/vstaging/mod.rs | 215 +++++++++++++++++++++++- 2 files changed, 215 insertions(+), 2 deletions(-) diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v7/mod.rs index a729d8cee4c7..18f52951986a 100644 --- a/polkadot/primitives/src/v7/mod.rs +++ b/polkadot/primitives/src/v7/mod.rs @@ -2052,7 +2052,7 @@ pub mod node_features { } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use bitvec::bitvec; use sp_core::sr25519; diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 27296213e611..ff138781153f 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -17,7 +17,11 @@ //! Staging Primitives. // Put any primitives used by staging APIs functions here -use crate::v7::*; +use super::{ + Balance, CoreIndex, CandidateCommitments, Hash, Id, ValidationCode, ValidationCodeHash, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, +}; +use sp_std::prelude::*; use codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -98,3 +102,212 @@ impl> Default for SchedulerParams } } } + +/// A unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateDescriptor { + /// The ID of the para this is a candidate for. + pub para_id: Id, + /// The hash of the relay-chain block this is executed in the context of. + pub relay_parent: H, + /// The core index where the candidate is backed. + pub core_index: CoreIndex, + /// Reserved bytes. + pub reserved28b: [u8; 27], + /// 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. + pub persisted_validation_data_hash: Hash, + /// The blake2-256 hash of the PoV. + pub pov_hash: Hash, + /// The root of a block's erasure encoding Merkle tree. + pub erasure_root: Hash, + /// Reserved bytes. + pub reserved64b: [u8; 64], + /// Hash of the para header that is being generated by this candidate. + pub para_head: Hash, + /// The blake2-256 hash of the validation code bytes. + pub validation_code_hash: ValidationCodeHash, +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum ExtendedCommitment { + #[codec(index = 0)] + CoreIndex(CoreIndex), +} + +pub const UMP_SEPARATOR: Vec = vec![]; + + +// /// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation. +// #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +// #[cfg_attr(feature = "std", derive(Default, Hash))] +// pub struct CandidateCommitments { +// /// Messages destined to be interpreted by the Relay chain itself. +// pub upward_messages: UpwardMessages, +// /// Horizontal messages sent by the parachain. +// pub horizontal_messages: HorizontalMessages, +// /// New validation code. +// pub new_validation_code: Option, +// /// The head-data produced as a result of execution. +// pub head_data: HeadData, +// /// The number of messages processed from the DMQ. +// pub processed_downward_messages: u32, +// /// The mark which specifies the block number up to which all inbound HRMP messages are +// /// processed. +// pub hrmp_watermark: N, +// } + +/// A candidate-receipt with commitments directly included. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CommittedCandidateReceipt { + /// The descriptor of the candidate. + descriptor: CandidateDescriptor, + /// The commitments of the candidate receipt. + commitments: CandidateCommitments, +} + +impl CandidateCommitments { + /// Returns the core index the candidate has commited to. + pub fn core_index(&self) -> Option { + /// We need at least 2 messages for the separator and core index + if self.upward_messages.len() < 2 { + return None + } + + let upward_commitments = self + .upward_messages + .iter() + .cloned() + .rev() + .take_while(|message| message != &UMP_SEPARATOR) + .collect::>(); + + // We didn't find the separator, no core index commitment. + if upward_commitments.len() == self.upward_messages.len() || + upward_commitments.is_empty() + { + return None + } + + // Use first commitment + let Some(message) = upward_commitments.into_iter().rev().next() else { return None }; + + match Commitment::decode(&mut message.as_slice()).ok()? { + Commitment::CoreIndex(core_index) => Some(core_index), + } + } +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum CandidateReceiptError { + /// The specified core index is invalid. + InvalidCoreIndex, + /// The core index in commitments doesnt match the one in descriptor + CoreIndexMismatch, +} + +impl CommittedCandidateReceipt { + /// Constructor from descriptor and commitments after sanity checking core index commitments. + pub fn new(descriptor: CandidateDescriptor, commitments: CandidateCommitments, n_cores: u32) -> Result { + // First check if we have a core index commitment + if commitments.core_index() != descriptor.core_index { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + match descriptor.core_index { + Some(core_index) => { + if core_index.0 > n_cores - 1 { + return Err(CandidateReceiptError::InvalidCoreIndex) + } + + Ok(Self { descriptor, commitments }) + }, + None => Ok(Self { descriptor, commitments }) + } + } + + /// Returns are reference to commitments + pub fn commitments(&self) -> &CandidateCommitments { + &self.commitments + } + + /// Returns a reference to the descriptor + pub fn descriptor(&self) -> &CandidateDescriptor { + &self.descriptor + } + + +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + v7::{ + tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, + CandidateReceipt as OldCandidateReceipt, Hash, HeadData, + }, + vstaging::CommittedCandidateReceipt, + }; + + pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceipt { + let zeros = Hash::zero(); + let reserved64b = [0; 64]; + + CommittedCandidateReceipt { + descriptor: CandidateDescriptor { + para_id: 0.into(), + relay_parent: zeros, + core_index: Some(CoreIndex(123)), + reserved28b: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved64b, + 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 mut old_ccr = dummy_old_committed_candidate_receipt(); + let mut new_ccr = dummy_committed_candidate_receipt(); + + assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); + assert_eq!(new_ccr.commitments().core_index(), None); + } + + #[test] + fn test_ump_commitment() { + let old_ccr = dummy_old_committed_candidate_receipt(); + let mut new_ccr = dummy_committed_candidate_receipt(); + + // 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(Commitment::CoreIndex(CoreIndex(123)).encode()); + + assert_eq!(new_ccr.descriptor.core_index, Some(CoreIndex(123))); + } +} From 3a29fdf3bf05f0558abccfb1a3f6be8c42473918 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 5 Aug 2024 19:43:14 +0300 Subject: [PATCH 02/40] WIP Signed-off-by: Andrei Sandu --- Cargo.lock | 2 + polkadot/primitives/Cargo.toml | 4 + polkadot/primitives/src/vstaging/mod.rs | 489 +++++++++++++++----- polkadot/primitives/test-helpers/src/lib.rs | 69 ++- 4 files changed, 459 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ebacc9ec5a8..8d0aa320c83c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13982,6 +13982,7 @@ dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", + "polkadot-primitives-test-helpers", "scale-info", "serde", "sp-api", @@ -13995,6 +13996,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", + "sp-std 14.0.0", ] [[package]] diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 8f7ec314ecff..09afc9662d62 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 = [ diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index ff138781153f..b259ba022772 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -18,15 +18,18 @@ // Put any primitives used by staging APIs functions here use super::{ - Balance, CoreIndex, CandidateCommitments, Hash, Id, ValidationCode, ValidationCodeHash, + Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, + CoreIndex, Hash, HashT, Id, Id as ParaId, ValidationCode, ValidationCodeHash, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, }; -use sp_std::prelude::*; +use alloc::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; use codec::{Decode, Encode}; use scale_info::TypeInfo; +use sp_api::Core; use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; +use sp_staking::SessionIndex; /// Scheduler configuration parameters. All coretime/ondemand parameters are here. #[derive( @@ -106,73 +109,312 @@ impl> Default for SchedulerParams /// A unique descriptor of the candidate receipt. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Hash))] -pub struct CandidateDescriptor { +pub struct CandidateDescriptorV2 { /// The ID of the para this is a candidate for. - pub para_id: Id, + para_id: ParaId, /// The hash of the relay-chain block this is executed in the context of. - pub relay_parent: H, + relay_parent: H, /// The core index where the candidate is backed. - pub core_index: CoreIndex, + core_index: u16, + /// The session index of the candidate relay parent. + session_index: SessionIndex, /// Reserved bytes. - pub reserved28b: [u8; 27], + reserved28b: [u8; 26], /// 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. - pub persisted_validation_data_hash: Hash, + persisted_validation_data_hash: Hash, /// The blake2-256 hash of the PoV. - pub pov_hash: Hash, + pov_hash: Hash, /// The root of a block's erasure encoding Merkle tree. - pub erasure_root: Hash, + erasure_root: Hash, /// Reserved bytes. - pub reserved64b: [u8; 64], + reserved64b: [u8; 64], /// Hash of the para header that is being generated by this candidate. - pub para_head: Hash, + para_head: Hash, /// The blake2-256 hash of the validation code bytes. - pub validation_code_hash: ValidationCodeHash, + validation_code_hash: ValidationCodeHash, } -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub enum ExtendedCommitment { - #[codec(index = 0)] - CoreIndex(CoreIndex), +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, + core_index: core_index.0 as u16, + session_index, + reserved28b: [0; 26], + persisted_validation_data_hash, + pov_hash, + erasure_root, + reserved64b: [0; 64], + para_head, + validation_code_hash, + } + } } +/// Version 1 API to access information stored by candidate descriptors. +pub trait CandidateApiV1 { + /// Returns the ID of the para this is a candidate for. + fn para_id(&self) -> Id; -pub const UMP_SEPARATOR: Vec = vec![]; + /// Returns the blake2-256 hash of the persisted validation data. + fn persisted_validation_data_hash(&self) -> Hash; + + /// Returns the blake2-256 hash of the PoV. + fn pov_hash(&self) -> Hash; + + /// Returns the root of a block's erasure encoding Merkle tree. + fn erasure_root(&self) -> Hash; + + /// Returns the hash of the para header generated by this candidate. + fn para_head(&self) -> Hash; + + /// Return the blake2-256 hash of the validation code bytes. + fn validation_code_hash(&self) -> ValidationCodeHash; +} + +/// Version 2 API to access additional information stored by candidate descriptors +pub trait CandidateApiV2 { + /// Returns the core index where the candidate is backed. + fn core_index(&self) -> CoreIndex; + + /// Returns the session index of the candidate relay parent. + fn session_index(&self) -> SessionIndex; +} + +impl CandidateApiV1 for CandidateDescriptor { + fn para_id(&self) -> Id { + self.para_id + } + + fn persisted_validation_data_hash(&self) -> Hash { + self.persisted_validation_data_hash + } + + fn pov_hash(&self) -> Hash { + self.pov_hash + } + + fn erasure_root(&self) -> Hash { + self.erasure_root + } + + fn para_head(&self) -> Hash { + self.para_head + } + + fn validation_code_hash(&self) -> ValidationCodeHash { + self.validation_code_hash + } +} + +impl CandidateApiV1 for CandidateDescriptorV2 { + fn para_id(&self) -> Id { + self.para_id + } + + fn persisted_validation_data_hash(&self) -> Hash { + self.persisted_validation_data_hash + } + fn pov_hash(&self) -> Hash { + self.pov_hash + } + + fn erasure_root(&self) -> Hash { + self.erasure_root + } + + fn para_head(&self) -> Hash { + self.para_head + } + + fn validation_code_hash(&self) -> ValidationCodeHash { + self.validation_code_hash + } +} -// /// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation. -// #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -// #[cfg_attr(feature = "std", derive(Default, Hash))] -// pub struct CandidateCommitments { -// /// Messages destined to be interpreted by the Relay chain itself. -// pub upward_messages: UpwardMessages, -// /// Horizontal messages sent by the parachain. -// pub horizontal_messages: HorizontalMessages, -// /// New validation code. -// pub new_validation_code: Option, -// /// The head-data produced as a result of execution. -// pub head_data: HeadData, -// /// The number of messages processed from the DMQ. -// pub processed_downward_messages: u32, -// /// The mark which specifies the block number up to which all inbound HRMP messages are -// /// processed. -// pub hrmp_watermark: N, -// } +impl CandidateApiV2 for CandidateDescriptorV2 { + fn core_index(&self) -> CoreIndex { + CoreIndex(self.core_index as u32) + } + + fn session_index(&self) -> SessionIndex { + self.session_index + } +} + +/// A candidate-receipt at version 2. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +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 CommittedCandidateReceipt { +pub struct CommittedCandidateReceiptV2 { /// The descriptor of the candidate. - descriptor: CandidateDescriptor, + pub descriptor: CandidateDescriptorV2, /// The commitments of the candidate receipt. - commitments: CandidateCommitments, + pub commitments: CandidateCommitments, } -impl CandidateCommitments { - /// Returns the core index the candidate has commited to. +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 [`CandidateReceipt`]? + 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)) + } +} + +/// A strictly increasing sequence number, tipically this would be the parachain block number. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct CoreSelector(pub BlockNumber); + +/// An offset in the relay chain claim queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct ClaimQueueOffset(pub u8); + +/// Default claim queue offset +pub const DEFAULT_CLAIM_QUEUE_OFFSET: ClaimQueueOffset = ClaimQueueOffset(1); + +#[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![]; + +/// A versioned unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum VersionedCandidateReceipt { + /// Version 1 of candidate receipt. + V1(super::CandidateReceipt), + /// Version 2 of candidate receipts with `core_index` and `session_index`. + V2(CandidateReceiptV2), +} + +/// A versioned unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum VersionedCommittedCandidateReceipt { + V1(super::CommittedCandidateReceipt), + V2(CommittedCandidateReceiptV2), +} + +impl VersionedCandidateReceipt { + /// Returns the core index the candidate has commited to. Returns `None`` if + /// the receipt is version 1. + pub fn core_index(&self) -> Option { + match self { + Self::V1(_receipt_v1) => None, + Self::V2(receipt_v2) => Some(receipt_v2.descriptor.core_index()), + } + } + + /// Returns the session index of the relay parent. Returns `None`` if + /// the receipt is version 1. + pub fn session_index(&self) -> Option { + match self { + Self::V1(_receipt_v1) => None, + Self::V2(receipt_v2) => Some(receipt_v2.descriptor.session_index()), + } + } +} + +impl VersionedCommittedCandidateReceipt { + /// Returns the core index the candidate has commited to. Returns `None`` if + /// the receipt is version 1. pub fn core_index(&self) -> Option { - /// We need at least 2 messages for the separator and core index + match self { + Self::V1(_receipt_v1) => None, + Self::V2(receipt_v2) => Some(receipt_v2.descriptor.core_index()), + } + } + + /// Returns the session index of the relay parent. Returns `None` if + /// the receipt is version 1. + pub fn session_index(&self) -> Option { + match self { + Self::V1(_receipt_v1) => None, + Self::V2(receipt_v2) => Some(receipt_v2.descriptor.session_index()), + } + } +} + +impl CandidateCommitments { + /// Returns the core selector and claim queue offset the candidate has commited to, if any. + pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { + // We need at least 2 messages for the separator and core index if self.upward_messages.len() < 2 { return None } @@ -186,60 +428,98 @@ impl CandidateCommitments { .collect::>(); // We didn't find the separator, no core index commitment. - if upward_commitments.len() == self.upward_messages.len() || - upward_commitments.is_empty() - { + if upward_commitments.len() == self.upward_messages.len() || upward_commitments.is_empty() { return None } // Use first commitment let Some(message) = upward_commitments.into_iter().rev().next() else { return None }; - match Commitment::decode(&mut message.as_slice()).ok()? { - Commitment::CoreIndex(core_index) => Some(core_index), + 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 doesnt match the one in descriptor CoreIndexMismatch, + /// The core selector or claim queue offset is invalid. + InvalidSelectedCore, } -impl CommittedCandidateReceipt { - /// Constructor from descriptor and commitments after sanity checking core index commitments. - pub fn new(descriptor: CandidateDescriptor, commitments: CandidateCommitments, n_cores: u32) -> Result { - // First check if we have a core index commitment - if commitments.core_index() != descriptor.core_index { - return Err(CandidateReceiptError::CoreIndexMismatch) - } - - match descriptor.core_index { - Some(core_index) => { - if core_index.0 > n_cores - 1 { - return Err(CandidateReceiptError::InvalidCoreIndex) - } - - Ok(Self { descriptor, commitments }) - }, - None => Ok(Self { descriptor, commitments }) - } +impl CommittedCandidateReceiptV2 { + /// Returns a v2 commited candidate receipt if the committed selected core + /// matches the core index in the descriptor. + pub fn new(descriptor: CandidateDescriptorV2, commitments: CandidateCommitments) -> Self { + Self { descriptor, commitments } } - /// Returns are reference to commitments + /// Returns a reference to commitments pub fn commitments(&self) -> &CandidateCommitments { &self.commitments } /// Returns a reference to the descriptor - pub fn descriptor(&self) -> &CandidateDescriptor { + pub fn descriptor(&self) -> &CandidateDescriptorV2 { &self.descriptor } - + /// Performs a sanity check of the receipt. + /// + /// Returns error if: + /// - descriptor core index is different than the core selected + /// by the commitments + /// - the core index is out of bounds wrt `n_cores`. + pub fn check( + &self, + n_cores: u32, + claimed_cores: Vec, + // TODO: consider promoting `ClaimQueueSnapshot` as primitive + claim_queue: BTreeMap>, + ) -> Result<(), CandidateReceiptError> { + let descriptor_core_index = CoreIndex(descriptor.core_index as u32); + let (core_selector, cq_offset) = self + .commitments + .selected_core() + .ok_or(Err(CandidateReceiptError::InvalidSelectedCore))?; + let para_id = self.descriptor.para_id; + + // TODO: bail out early if cq_offset > depth of claim queue. + + // Get a vec of the core indices the parachain is assigned to at `cq_offset`. + let assigned_cores = claim_queue + .iter() + .filter_map(|(core_index, queue)| { + let queued_para = queue.get(cq_offset)?; + + if queued_para == para_id { + Some(core_index) + } else { + None + } + }) + .cloned() + .collect::>(); + + let core_index = *assigned_cores + .get(core_selector.0 as usize % assigned_cores.len()) + .expect("provided index is always less than queue len; qed"); + + if core_index != Some(descriptor_core_index) { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + if descriptor_core_index.0 > n_cores - 1 { + return Err(CandidateReceiptError::InvalidCoreIndex) + } + + Ok(()) + } } #[cfg(test)] @@ -248,43 +528,46 @@ mod tests { use crate::{ v7::{ tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, - CandidateReceipt as OldCandidateReceipt, Hash, HeadData, + CandidateDescriptor, CandidateReceipt as OldCandidateReceipt, + CommittedCandidateReceipt, Hash, HeadData, }, - vstaging::CommittedCandidateReceipt, + vstaging::CommittedCandidateReceiptV2, }; - - pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceipt { - let zeros = Hash::zero(); - let reserved64b = [0; 64]; - - CommittedCandidateReceipt { - descriptor: CandidateDescriptor { - para_id: 0.into(), - relay_parent: zeros, - core_index: Some(CoreIndex(123)), - reserved28b: Default::default(), - persisted_validation_data_hash: zeros, - pov_hash: zeros, - erasure_root: zeros, - reserved64b, - 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, - }, - } - } + use polkadot_primitives_test_helpers::dummy_committed_candidate_receipt_v2; + + // pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceiptV2 { + // let zeros = Hash::zero(); + // let reserved64b = [0; 64]; + + // CommittedCandidateReceiptV2 { + // descriptor: CandidateDescriptorV2 { + // para_id: 0.into(), + // relay_parent: zeros, + // core_index: 123, + // session_index: 1, + // reserved28b: Default::default(), + // persisted_validation_data_hash: zeros, + // pov_hash: zeros, + // erasure_root: zeros, + // reserved64b, + // 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 mut old_ccr = dummy_old_committed_candidate_receipt(); - let mut new_ccr = dummy_committed_candidate_receipt(); + let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::zero()); assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); assert_eq!(new_ccr.commitments().core_index(), None); @@ -292,8 +575,7 @@ mod tests { #[test] fn test_ump_commitment() { - let old_ccr = dummy_old_committed_candidate_receipt(); - let mut new_ccr = dummy_committed_candidate_receipt(); + let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::zero()); // XCM messages new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); @@ -306,8 +588,9 @@ mod tests { new_ccr .commitments .upward_messages - .force_push(Commitment::CoreIndex(CoreIndex(123)).encode()); + .force_push(UMPSignal::OnCore(CoreIndex(123)).encode()); - assert_eq!(new_ccr.descriptor.core_index, Some(CoreIndex(123))); + let new_ccr = VersionedCommittedCandidateReceipt::V2(new_ccr); + assert_eq!(new_ccr.core_index(), Some(CoreIndex(123))); } } diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index d43cf3317e57..c06c0420fd70 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,9 +23,13 @@ //! 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, + VersionedCandidateReceipt, + }, 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 +46,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 +64,14 @@ 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 +144,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]) @@ -232,6 +269,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, From 4a53577abb8234e4174f4ab1545012f7cf066bf0 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 7 Aug 2024 17:01:00 +0300 Subject: [PATCH 03/40] Working version. Signed-off-by: Andrei Sandu --- polkadot/primitives/src/v7/mod.rs | 1 - polkadot/primitives/src/vstaging/mod.rs | 572 +++++++++++++++----- polkadot/primitives/test-helpers/src/lib.rs | 8 +- 3 files changed, 452 insertions(+), 129 deletions(-) diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v7/mod.rs index 18f52951986a..4b7abdb311f5 100644 --- a/polkadot/primitives/src/v7/mod.rs +++ b/polkadot/primitives/src/v7/mod.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . //! `V7` Primitives. - use alloc::{ vec, vec::{IntoIter, Vec}, diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index b259ba022772..749645753991 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,18 +15,20 @@ // along with Polkadot. If not, see . //! Staging Primitives. +use crate::{CandidateReceipt, CommittedCandidateReceipt, ValidityAttestation}; // Put any primitives used by staging APIs functions here use super::{ Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, - CoreIndex, Hash, HashT, Id, Id as ParaId, ValidationCode, ValidationCodeHash, + CollatorId, CollatorSignature, CoreIndex, Hash, HashT, Id, Id as ParaId, ValidationCodeHash, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, }; +use bitvec::prelude::*; +use sp_application_crypto::ByteArray; use alloc::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; use codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_api::Core; use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; use sp_staking::SessionIndex; @@ -119,7 +121,7 @@ pub struct CandidateDescriptorV2 { /// The session index of the candidate relay parent. session_index: SessionIndex, /// Reserved bytes. - reserved28b: [u8; 26], + reserved26b: [u8; 26], /// 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. @@ -154,7 +156,7 @@ impl CandidateDescriptorV2 { relay_parent, core_index: core_index.0 as u16, session_index, - reserved28b: [0; 26], + reserved26b: [0; 26], persisted_validation_data_hash, pov_hash, erasure_root, @@ -183,81 +185,37 @@ pub trait CandidateApiV1 { /// Return the blake2-256 hash of the validation code bytes. fn validation_code_hash(&self) -> ValidationCodeHash; + + /// The collator's sr25519 public key. + fn collator(&self) -> Option<&CollatorId>; + + /// The parachain index, the relay parent, the validation data hash, and the `pov_hash`. + fn signature(&self) -> Option<&CollatorSignature>; } /// Version 2 API to access additional information stored by candidate descriptors pub trait CandidateApiV2 { /// Returns the core index where the candidate is backed. - fn core_index(&self) -> CoreIndex; + fn core_index(&self) -> Option; /// Returns the session index of the candidate relay parent. - fn session_index(&self) -> SessionIndex; -} - -impl CandidateApiV1 for CandidateDescriptor { - fn para_id(&self) -> Id { - self.para_id - } - - fn persisted_validation_data_hash(&self) -> Hash { - self.persisted_validation_data_hash - } - - fn pov_hash(&self) -> Hash { - self.pov_hash - } - - fn erasure_root(&self) -> Hash { - self.erasure_root - } - - fn para_head(&self) -> Hash { - self.para_head - } - - fn validation_code_hash(&self) -> ValidationCodeHash { - self.validation_code_hash - } + fn session_index(&self) -> Option; } -impl CandidateApiV1 for CandidateDescriptorV2 { - fn para_id(&self) -> Id { - self.para_id - } - - fn persisted_validation_data_hash(&self) -> Hash { - self.persisted_validation_data_hash - } - - fn pov_hash(&self) -> Hash { - self.pov_hash - } - - fn erasure_root(&self) -> Hash { - self.erasure_root - } - - fn para_head(&self) -> Hash { - self.para_head - } - - fn validation_code_hash(&self) -> ValidationCodeHash { - self.validation_code_hash - } -} impl CandidateApiV2 for CandidateDescriptorV2 { - fn core_index(&self) -> CoreIndex { - CoreIndex(self.core_index as u32) + fn core_index(&self) -> Option { + Some(CoreIndex(self.core_index as u32)) } - fn session_index(&self) -> SessionIndex { - self.session_index + fn session_index(&self) -> Option { + Some(self.session_index) } } /// 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, @@ -356,28 +314,84 @@ pub enum UMPSignal { pub const UMP_SEPARATOR: Vec = vec![]; /// A versioned unique descriptor of the candidate receipt. -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub enum VersionedCandidateReceipt { +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +pub enum VersionedCandidateReceipt { /// Version 1 of candidate receipt. - V1(super::CandidateReceipt), + V1(CandidateReceipt), /// Version 2 of candidate receipts with `core_index` and `session_index`. - V2(CandidateReceiptV2), + V2(CandidateReceiptV2), +} + +impl Encode for VersionedCandidateReceipt { + fn encode(&self) -> Vec { + match self { + VersionedCandidateReceipt::V1(inner) => inner.encode(), + VersionedCandidateReceipt::V2(inner) => inner.encode(), + } + } +} + +impl Decode for VersionedCandidateReceipt { + fn decode(input: &mut I) -> Result { + let descriptor: CandidateDescriptorV2 = Decode::decode(input)?; + let commitments_hash = Hash::decode(input)?; + + // Check if descriptor is v2. + if descriptor.reserved64b == [0u8; 64] && descriptor.reserved26b == [0u8; 26] { + return Ok(VersionedCandidateReceipt::V2(CandidateReceiptV2 { + descriptor, + commitments_hash, + })) + } + + // Fall back to v1. + // TODO: optimize, an additional encode and decode is not nice. + let descriptor = CandidateDescriptor::decode(&mut descriptor.encode().as_slice())?; + + Ok(VersionedCandidateReceipt::V1(CandidateReceipt { descriptor, commitments_hash })) + } +} + +impl Decode for VersionedCommittedCandidateReceipt { + fn decode(input: &mut I) -> Result { + let descriptor = CandidateDescriptorV2::decode(input)?; + let commitments = CandidateCommitments::decode(input)?; + + // Check if descriptor is v2. + if descriptor.reserved64b == [0u8; 64] && descriptor.reserved26b == [0u8; 26] { + return Ok(VersionedCommittedCandidateReceipt::V2(CommittedCandidateReceiptV2 { + descriptor, + commitments, + })) + } + + // Fall back to v1. + // TODO: optimize, an additional encode and decode is not nice. + let descriptor = CandidateDescriptor::decode(&mut descriptor.encode().as_slice())?; + + Ok(VersionedCommittedCandidateReceipt::V1(CommittedCandidateReceipt { + descriptor, + commitments, + })) + } } /// A versioned unique descriptor of the candidate receipt. -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub enum VersionedCommittedCandidateReceipt { +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +pub enum VersionedCommittedCandidateReceipt { + /// Version 1 V1(super::CommittedCandidateReceipt), + /// Version 2 V2(CommittedCandidateReceiptV2), } -impl VersionedCandidateReceipt { +impl VersionedCandidateReceipt { /// Returns the core index the candidate has commited to. Returns `None`` if /// the receipt is version 1. pub fn core_index(&self) -> Option { match self { Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => Some(receipt_v2.descriptor.core_index()), + Self::V2(receipt_v2) => receipt_v2.descriptor.core_index(), } } @@ -386,18 +400,29 @@ impl VersionedCandidateReceipt { pub fn session_index(&self) -> Option { match self { Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => Some(receipt_v2.descriptor.session_index()), + Self::V2(receipt_v2) => receipt_v2.descriptor.session_index(), + } + } + + /// Computes the blake2-256 hash of the receipt. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + match self { + Self::V1(receipt_v1) => receipt_v1.hash(), + Self::V2(receipt_v2) => receipt_v2.hash(), } } } -impl VersionedCommittedCandidateReceipt { +impl VersionedCommittedCandidateReceipt { /// Returns the core index the candidate has commited to. Returns `None`` if /// the receipt is version 1. pub fn core_index(&self) -> Option { match self { Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => Some(receipt_v2.descriptor.core_index()), + Self::V2(receipt_v2) => receipt_v2.descriptor.core_index(), } } @@ -406,7 +431,24 @@ impl VersionedCommittedCandidateReceipt { pub fn session_index(&self) -> Option { match self { Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => Some(receipt_v2.descriptor.session_index()), + Self::V2(receipt_v2) => receipt_v2.descriptor.session_index(), + } + } + + /// Convert to a plain `CandidateReceipt`. + pub fn to_plain(&self) -> VersionedCandidateReceipt { + match self { + Self::V1(receipt) => VersionedCandidateReceipt::V1(receipt.to_plain()), + Self::V2(receipt) => VersionedCandidateReceipt::V2(receipt.to_plain()), + } + } + + /// Returns the candidate commitments. + /// Convert to a plain `CandidateReceipt`. + pub fn commitments(&self) -> &CandidateCommitments { + match self { + Self::V1(receipt) => &receipt.commitments, + Self::V2(receipt) => &receipt.commitments, } } } @@ -450,6 +492,44 @@ pub enum CandidateReceiptError { 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, +} + +macro_rules! impl_candidate_api_v1 { + ($field:ident, $type:ident) => { + fn $field(&self) -> $type { + match self { + Self::V1(receipt) => receipt.descriptor.$field, + Self::V2(receipt) => receipt.descriptor.$field, + } + } + }; +} + +impl CandidateApiV1 for VersionedCommittedCandidateReceipt { + impl_candidate_api_v1!(erasure_root, Hash); + impl_candidate_api_v1!(para_head, Hash); + impl_candidate_api_v1!(para_id, ParaId); + impl_candidate_api_v1!(persisted_validation_data_hash, Hash); + impl_candidate_api_v1!(pov_hash, Hash); + impl_candidate_api_v1!(validation_code_hash, ValidationCodeHash); + + fn collator(&self) -> Option<&CollatorId> { + match self { + Self::V1(receipt) => Some(&receipt.descriptor.collator), + Self::V2(receipt) => None, + } + } + + fn signature(&self) -> Option<&CollatorSignature> { + match self { + Self::V1(receipt) => Some(&receipt.descriptor.signature), + Self::V2(receipt) => None, + } + } } impl CommittedCandidateReceiptV2 { @@ -478,26 +558,35 @@ impl CommittedCandidateReceiptV2 { pub fn check( &self, n_cores: u32, - claimed_cores: Vec, // TODO: consider promoting `ClaimQueueSnapshot` as primitive - claim_queue: BTreeMap>, + claim_queue: &BTreeMap>, ) -> Result<(), CandidateReceiptError> { - let descriptor_core_index = CoreIndex(descriptor.core_index as u32); - let (core_selector, cq_offset) = self - .commitments - .selected_core() - .ok_or(Err(CandidateReceiptError::InvalidSelectedCore))?; + if claim_queue.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + + let claim_queue_depth = claim_queue + .first_key_value() + .ok_or(CandidateReceiptError::NoAssignment)? + .1 + .len(); + + 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 para_id = self.descriptor.para_id; - // TODO: bail out early if cq_offset > depth of claim queue. - + if cq_offset.0 as usize >= claim_queue_depth { + return Err(CandidateReceiptError::InvalidSelectedCore) + } + // Get a vec of the core indices the parachain is assigned to at `cq_offset`. let assigned_cores = claim_queue .iter() .filter_map(|(core_index, queue)| { - let queued_para = queue.get(cq_offset)?; + let queued_para = queue.get(cq_offset.0 as usize)?; - if queued_para == para_id { + if queued_para == ¶_id { Some(core_index) } else { None @@ -506,11 +595,15 @@ impl CommittedCandidateReceiptV2 { .cloned() .collect::>(); + if assigned_cores.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + let core_index = *assigned_cores .get(core_selector.0 as usize % assigned_cores.len()) .expect("provided index is always less than queue len; qed"); - if core_index != Some(descriptor_core_index) { + if core_index != descriptor_core_index { return Err(CandidateReceiptError::CoreIndexMismatch) } @@ -522,62 +615,230 @@ impl CommittedCandidateReceiptV2 { } } +impl From> for VersionedCommittedCandidateReceipt { + fn from(value: CommittedCandidateReceipt) -> Self { + Self::V1(value) + } +} + +impl From> for VersionedCommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self::V2(value) + } +} + +impl From> for VersionedCandidateReceipt { + fn from(value: CandidateReceipt) -> Self { + Self::V1(value) + } +} + +impl From> for VersionedCandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self::V2(value) + } +} + +/// A backed (or backable, depending on context) candidate. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BackedCandidate { + /// The candidate referred to. + candidate: VersionedCommittedCandidateReceipt, + /// 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, +} + +impl BackedCandidate { + /// Constructor + pub fn new( + candidate: VersionedCommittedCandidateReceipt, + 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) -> &VersionedCommittedCandidateReceipt { + &self.candidate + } + + /// 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) -> VersionedCandidateReceipt + 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); + } + } +} + #[cfg(test)] mod tests { + use core::prelude::v1; + use super::*; use crate::{ v7::{ tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, CandidateDescriptor, CandidateReceipt as OldCandidateReceipt, - CommittedCandidateReceipt, Hash, HeadData, + CommittedCandidateReceipt, Hash, HeadData, ValidationCode, + }, + vstaging::{ + CandidateApiV1, CandidateApiV2, CandidateDescriptorV2, CommittedCandidateReceiptV2, }, - vstaging::CommittedCandidateReceiptV2, }; - use polkadot_primitives_test_helpers::dummy_committed_candidate_receipt_v2; - - // pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceiptV2 { - // let zeros = Hash::zero(); - // let reserved64b = [0; 64]; - - // CommittedCandidateReceiptV2 { - // descriptor: CandidateDescriptorV2 { - // para_id: 0.into(), - // relay_parent: zeros, - // core_index: 123, - // session_index: 1, - // reserved28b: Default::default(), - // persisted_validation_data_hash: zeros, - // pov_hash: zeros, - // erasure_root: zeros, - // reserved64b, - // 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, - // }, - // } - // } + // use polkadot_primitives_test_helpers::dummy_committed_candidate_receipt_v2; + + pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { + let zeros = Hash::zero(); + let reserved64b = [0; 64]; + + CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2 { + para_id: 0.into(), + relay_parent: zeros, + core_index: 123, + session_index: 1, + reserved26b: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved64b, + 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 mut old_ccr = dummy_old_committed_candidate_receipt(); - let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::zero()); + 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()); - assert_eq!(new_ccr.commitments().core_index(), None); + + 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 test_ump_commitment() { - let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::zero()); + 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 mut claim_queue = BTreeMap::new(); + claim_queue.insert( + new_ccr.descriptor().core_index().unwrap(), + vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), + ); + + assert_eq!(new_ccr.check(200, &claim_queue), Ok(())); + } - // XCM messages + #[test] + fn test_versioned_receipt() { + 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]); @@ -588,9 +849,70 @@ mod tests { new_ccr .commitments .upward_messages - .force_push(UMPSignal::OnCore(CoreIndex(123)).encode()); + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let mut claim_queue = BTreeMap::new(); + claim_queue.insert( + new_ccr.descriptor().core_index().unwrap(), + vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), + ); + + let encoded_ccr = new_ccr.encode(); + let versioned_ccr = + VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(versioned_ccr.core_index(), Some(CoreIndex(123))); + + if let VersionedCommittedCandidateReceipt::V2(ref v2_receipt) = versioned_ccr { + assert_eq!(v2_receipt.check(200, &claim_queue), Ok(())); + } + + assert_eq!(new_ccr.hash(), versioned_ccr.to_plain().hash()); + } + + #[test] + fn test_backward_compatible() { + // 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 versioned_ccr = + VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); + + // Since collator sig and id are zeroed, it was decoded as V2. + // We expect the check to fail in such case because there will be no `SelectCore` + // commitment. + let mut claim_queue = BTreeMap::new(); + claim_queue.insert( + versioned_ccr.core_index().unwrap(), + vec![2.into(), versioned_ccr.para_id(), 3.into()].into(), + ); + + if let VersionedCommittedCandidateReceipt::V2(ref v2_receipt) = versioned_ccr { + assert_eq!( + v2_receipt.check(200, &claim_queue), + Err(CandidateReceiptError::NoCoreSelected) + ); + } else { + panic!("Should have decoded as V2") + } + + // Adding collator signature should make it decode as v1. + old_ccr.descriptor.signature = + CollatorSignature::from_slice(&vec![99u8; 64]).expect("64 bytes; qed"); + let encoded_ccr: Vec = old_ccr.encode(); + let versioned_ccr = + VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); + + if let VersionedCommittedCandidateReceipt::V1(ref v1_receipt) = versioned_ccr { + assert_eq!(v1_receipt.descriptor.signature, old_ccr.descriptor.signature); + } else { + panic!("Should have decoded as V1") + } - let new_ccr = VersionedCommittedCandidateReceipt::V2(new_ccr); - assert_eq!(new_ccr.core_index(), Some(CoreIndex(123))); + assert_eq!(versioned_ccr.core_index(), None); + assert_eq!(versioned_ccr.para_id(), ParaId::new(1000)); + assert_eq!(old_ccr.hash(), versioned_ccr.to_plain().hash()); } } diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index c06c0420fd70..c262f16df077 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -65,7 +65,9 @@ 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 { +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()), @@ -145,7 +147,7 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD } /// Create a v2 candidate descriptor with filler data. -pub fn dummy_candidate_descriptor_v2(relay_parent: H) -> CandidateDescriptorV2 { +pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { let invalid = Hash::zero(); let descriptor = make_valid_candidate_descriptor_v2( 1.into(), @@ -270,7 +272,7 @@ pub fn make_valid_candidate_descriptor>( } /// Create a v2 candidate descriptor. -pub fn make_valid_candidate_descriptor_v2( +pub fn make_valid_candidate_descriptor_v2>( para_id: ParaId, relay_parent: H, core_index: CoreIndex, From 8285ae79e16e62a373d704fed328abf702ed125e Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 8 Aug 2024 17:09:35 +0300 Subject: [PATCH 04/40] Better version Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 459 +++++++------------- polkadot/primitives/test-helpers/src/lib.rs | 5 +- 2 files changed, 159 insertions(+), 305 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 749645753991..03075e820524 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,12 +15,13 @@ // along with Polkadot. If not, see . //! Staging Primitives. -use crate::{CandidateReceipt, CommittedCandidateReceipt, ValidityAttestation}; +use crate::ValidityAttestation; // Put any primitives used by staging APIs functions here use super::{ - Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, - CollatorId, CollatorSignature, CoreIndex, Hash, HashT, Id, Id as ParaId, ValidationCodeHash, + Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, + CollatorId, CollatorSignature, CoreIndex, Hash, HashT, Header, Id, Id as ParaId, + MultiDisputeStatementSet, UncheckedSignedAvailabilityBitfields, ValidationCodeHash, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, }; use bitvec::prelude::*; @@ -31,6 +32,7 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; +use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; /// Scheduler configuration parameters. All coretime/ondemand parameters are here. @@ -108,6 +110,22 @@ impl> Default for SchedulerParams } } +/// A type representing the version of the candidate descriptor and internal version number. +#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct InternalVersion(pub u8); + +/// A type representing the version of the candidate descriptor and internal version number. +/// For `V1`` the internal version number stores the first byte of the `CollatorId`. +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub enum CandidateDescriptorVersion { + /// The old candidate descriptor version. + V1, + /// Introduced with `CandidateDescriptorV2` + V2, +} + /// A unique descriptor of the candidate receipt. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Hash))] @@ -116,12 +134,14 @@ pub struct CandidateDescriptorV2 { para_id: ParaId, /// The hash of the relay-chain block this is executed in the context of. relay_parent: H, + /// Version field + 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. - reserved26b: [u8; 26], + reserved25b: [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. @@ -154,9 +174,10 @@ impl CandidateDescriptorV2 { Self { para_id, relay_parent, + version: InternalVersion(0), core_index: core_index.0 as u16, session_index, - reserved26b: [0; 26], + reserved25b: [0; 25], persisted_validation_data_hash, pov_hash, erasure_root, @@ -166,52 +187,6 @@ impl CandidateDescriptorV2 { } } } -/// Version 1 API to access information stored by candidate descriptors. -pub trait CandidateApiV1 { - /// Returns the ID of the para this is a candidate for. - fn para_id(&self) -> Id; - - /// Returns the blake2-256 hash of the persisted validation data. - fn persisted_validation_data_hash(&self) -> Hash; - - /// Returns the blake2-256 hash of the PoV. - fn pov_hash(&self) -> Hash; - - /// Returns the root of a block's erasure encoding Merkle tree. - fn erasure_root(&self) -> Hash; - - /// Returns the hash of the para header generated by this candidate. - fn para_head(&self) -> Hash; - - /// Return the blake2-256 hash of the validation code bytes. - fn validation_code_hash(&self) -> ValidationCodeHash; - - /// The collator's sr25519 public key. - fn collator(&self) -> Option<&CollatorId>; - - /// The parachain index, the relay parent, the validation data hash, and the `pov_hash`. - fn signature(&self) -> Option<&CollatorSignature>; -} - -/// Version 2 API to access additional information stored by candidate descriptors -pub trait CandidateApiV2 { - /// Returns the core index where the candidate is backed. - fn core_index(&self) -> Option; - - /// Returns the session index of the candidate relay parent. - fn session_index(&self) -> Option; -} - - -impl CandidateApiV2 for CandidateDescriptorV2 { - fn core_index(&self) -> Option { - Some(CoreIndex(self.core_index as u32)) - } - - fn session_index(&self) -> Option { - Some(self.session_index) - } -} /// A candidate-receipt at version 2. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] @@ -285,9 +260,9 @@ impl PartialOrd for CommittedCandidateReceiptV2 { impl Ord for CommittedCandidateReceiptV2 { fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.descriptor() + self.descriptor .para_id - .cmp(&other.descriptor().para_id) + .cmp(&other.descriptor.para_id) .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data)) } } @@ -303,6 +278,7 @@ pub struct ClaimQueueOffset(pub u8); /// Default claim queue offset pub const DEFAULT_CLAIM_QUEUE_OFFSET: ClaimQueueOffset = ClaimQueueOffset(1); +/// 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. @@ -313,146 +289,6 @@ pub enum UMPSignal { /// Separator between `XCM` and `UMPSignal`. pub const UMP_SEPARATOR: Vec = vec![]; -/// A versioned unique descriptor of the candidate receipt. -#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] -pub enum VersionedCandidateReceipt { - /// Version 1 of candidate receipt. - V1(CandidateReceipt), - /// Version 2 of candidate receipts with `core_index` and `session_index`. - V2(CandidateReceiptV2), -} - -impl Encode for VersionedCandidateReceipt { - fn encode(&self) -> Vec { - match self { - VersionedCandidateReceipt::V1(inner) => inner.encode(), - VersionedCandidateReceipt::V2(inner) => inner.encode(), - } - } -} - -impl Decode for VersionedCandidateReceipt { - fn decode(input: &mut I) -> Result { - let descriptor: CandidateDescriptorV2 = Decode::decode(input)?; - let commitments_hash = Hash::decode(input)?; - - // Check if descriptor is v2. - if descriptor.reserved64b == [0u8; 64] && descriptor.reserved26b == [0u8; 26] { - return Ok(VersionedCandidateReceipt::V2(CandidateReceiptV2 { - descriptor, - commitments_hash, - })) - } - - // Fall back to v1. - // TODO: optimize, an additional encode and decode is not nice. - let descriptor = CandidateDescriptor::decode(&mut descriptor.encode().as_slice())?; - - Ok(VersionedCandidateReceipt::V1(CandidateReceipt { descriptor, commitments_hash })) - } -} - -impl Decode for VersionedCommittedCandidateReceipt { - fn decode(input: &mut I) -> Result { - let descriptor = CandidateDescriptorV2::decode(input)?; - let commitments = CandidateCommitments::decode(input)?; - - // Check if descriptor is v2. - if descriptor.reserved64b == [0u8; 64] && descriptor.reserved26b == [0u8; 26] { - return Ok(VersionedCommittedCandidateReceipt::V2(CommittedCandidateReceiptV2 { - descriptor, - commitments, - })) - } - - // Fall back to v1. - // TODO: optimize, an additional encode and decode is not nice. - let descriptor = CandidateDescriptor::decode(&mut descriptor.encode().as_slice())?; - - Ok(VersionedCommittedCandidateReceipt::V1(CommittedCandidateReceipt { - descriptor, - commitments, - })) - } -} - -/// A versioned unique descriptor of the candidate receipt. -#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] -pub enum VersionedCommittedCandidateReceipt { - /// Version 1 - V1(super::CommittedCandidateReceipt), - /// Version 2 - V2(CommittedCandidateReceiptV2), -} - -impl VersionedCandidateReceipt { - /// Returns the core index the candidate has commited to. Returns `None`` if - /// the receipt is version 1. - pub fn core_index(&self) -> Option { - match self { - Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => receipt_v2.descriptor.core_index(), - } - } - - /// Returns the session index of the relay parent. Returns `None`` if - /// the receipt is version 1. - pub fn session_index(&self) -> Option { - match self { - Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => receipt_v2.descriptor.session_index(), - } - } - - /// Computes the blake2-256 hash of the receipt. - pub fn hash(&self) -> CandidateHash - where - H: Encode, - { - match self { - Self::V1(receipt_v1) => receipt_v1.hash(), - Self::V2(receipt_v2) => receipt_v2.hash(), - } - } -} - -impl VersionedCommittedCandidateReceipt { - /// Returns the core index the candidate has commited to. Returns `None`` if - /// the receipt is version 1. - pub fn core_index(&self) -> Option { - match self { - Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => receipt_v2.descriptor.core_index(), - } - } - - /// Returns the session index of the relay parent. Returns `None` if - /// the receipt is version 1. - pub fn session_index(&self) -> Option { - match self { - Self::V1(_receipt_v1) => None, - Self::V2(receipt_v2) => receipt_v2.descriptor.session_index(), - } - } - - /// Convert to a plain `CandidateReceipt`. - pub fn to_plain(&self) -> VersionedCandidateReceipt { - match self { - Self::V1(receipt) => VersionedCandidateReceipt::V1(receipt.to_plain()), - Self::V2(receipt) => VersionedCandidateReceipt::V2(receipt.to_plain()), - } - } - - /// Returns the candidate commitments. - /// Convert to a plain `CandidateReceipt`. - pub fn commitments(&self) -> &CandidateCommitments { - match self { - Self::V1(receipt) => &receipt.commitments, - Self::V2(receipt) => &receipt.commitments, - } - } -} - impl CandidateCommitments { /// Returns the core selector and claim queue offset the candidate has commited to, if any. pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { @@ -498,58 +334,84 @@ pub enum CandidateReceiptError { NoCoreSelected, } -macro_rules! impl_candidate_api_v1 { +macro_rules! impl_getter { ($field:ident, $type:ident) => { fn $field(&self) -> $type { - match self { - Self::V1(receipt) => receipt.descriptor.$field, - Self::V2(receipt) => receipt.descriptor.$field, - } + self.$field } }; } -impl CandidateApiV1 for VersionedCommittedCandidateReceipt { - impl_candidate_api_v1!(erasure_root, Hash); - impl_candidate_api_v1!(para_head, Hash); - impl_candidate_api_v1!(para_id, ParaId); - impl_candidate_api_v1!(persisted_validation_data_hash, Hash); - impl_candidate_api_v1!(pov_hash, Hash); - impl_candidate_api_v1!(validation_code_hash, ValidationCodeHash); - - fn collator(&self) -> Option<&CollatorId> { - match self { - Self::V1(receipt) => Some(&receipt.descriptor.collator), - Self::V2(receipt) => None, +impl CandidateDescriptorV2 { + impl_getter!(erasure_root, Hash); + impl_getter!(para_head, Hash); + impl_getter!(relay_parent, Hash); + 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.reserved64b != [0u8; 64] || self.reserved25b != [0u8; 25] { + return CandidateDescriptorVersion::V1 + } + + match self.version.0 { + 0 => CandidateDescriptorVersion::V2, + _ => CandidateDescriptorVersion::V1, } } - fn signature(&self) -> Option<&CollatorSignature> { - match self { - Self::V1(receipt) => Some(&receipt.descriptor.signature), - Self::V2(receipt) => None, + /// Returns the collator id if this is a v1 `CandidateDescriptor` + pub fn collator(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + let mut collator_id = vec![self.version.0]; + let core_index: [u8; 2] = unsafe { std::mem::transmute(self.core_index) }; + let session_index: [u8; 4] = unsafe { std::mem::transmute(self.session_index) }; + + collator_id.append(&mut core_index.as_slice().to_vec()); + collator_id.append(&mut session_index.as_slice().to_vec()); + collator_id.append(&mut self.reserved25b.as_slice().to_vec()); + + return Some(CollatorId::from_slice(&collator_id.as_slice()).ok()?) } + + None } -} -impl CommittedCandidateReceiptV2 { - /// Returns a v2 commited candidate receipt if the committed selected core - /// matches the core index in the descriptor. - pub fn new(descriptor: CandidateDescriptorV2, commitments: CandidateCommitments) -> Self { - Self { descriptor, commitments } + /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. + pub fn signature(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return Some(CollatorSignature::from_slice(self.reserved64b.as_slice()).ok()?) + } + + None } - /// Returns a reference to commitments - pub fn commitments(&self) -> &CandidateCommitments { - &self.commitments + /// 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 a reference to the descriptor - pub fn descriptor(&self) -> &CandidateDescriptorV2 { - &self.descriptor + /// 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) } +} - /// Performs a sanity check of the receipt. +impl CommittedCandidateReceiptV2 { + /// Performs a sanity check of the descriptor and commitment. /// /// Returns error if: /// - descriptor core index is different than the core selected @@ -615,35 +477,11 @@ impl CommittedCandidateReceiptV2 { } } -impl From> for VersionedCommittedCandidateReceipt { - fn from(value: CommittedCandidateReceipt) -> Self { - Self::V1(value) - } -} - -impl From> for VersionedCommittedCandidateReceipt { - fn from(value: CommittedCandidateReceiptV2) -> Self { - Self::V2(value) - } -} - -impl From> for VersionedCandidateReceipt { - fn from(value: CandidateReceipt) -> Self { - Self::V1(value) - } -} - -impl From> for VersionedCandidateReceipt { - fn from(value: CandidateReceiptV2) -> Self { - Self::V2(value) - } -} - /// A backed (or backable, depending on context) candidate. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct BackedCandidate { /// The candidate referred to. - candidate: VersionedCommittedCandidateReceipt, + 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 @@ -652,10 +490,23 @@ pub struct BackedCandidate { 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: VersionedCommittedCandidateReceipt, + candidate: CommittedCandidateReceiptV2, validity_votes: Vec, validator_indices: BitVec, core_index: Option, @@ -668,7 +519,7 @@ impl BackedCandidate { } /// Get a reference to the committed candidate receipt of the candidate. - pub fn candidate(&self) -> &VersionedCommittedCandidateReceipt { + pub fn candidate(&self) -> &CommittedCandidateReceiptV2 { &self.candidate } @@ -691,7 +542,7 @@ impl BackedCandidate { } /// Get this candidate's receipt. - pub fn receipt(&self) -> VersionedCandidateReceipt + pub fn receipt(&self) -> CandidateReceiptV2 where H: Clone, { @@ -743,20 +594,14 @@ impl BackedCandidate { #[cfg(test)] mod tests { - use core::prelude::v1; - use super::*; use crate::{ v7::{ tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, - CandidateDescriptor, CandidateReceipt as OldCandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, ValidationCode, }, - vstaging::{ - CandidateApiV1, CandidateApiV2, CandidateDescriptorV2, CommittedCandidateReceiptV2, - }, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2}, }; - // use polkadot_primitives_test_helpers::dummy_committed_candidate_receipt_v2; pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { let zeros = Hash::zero(); @@ -766,9 +611,10 @@ mod tests { descriptor: CandidateDescriptorV2 { para_id: 0.into(), relay_parent: zeros, + version: InternalVersion(0), core_index: 123, session_index: 1, - reserved26b: Default::default(), + reserved25b: Default::default(), persisted_validation_data_hash: zeros, pov_hash: zeros, erasure_root: zeros, @@ -825,7 +671,7 @@ mod tests { let mut claim_queue = BTreeMap::new(); claim_queue.insert( - new_ccr.descriptor().core_index().unwrap(), + new_ccr.descriptor.core_index().unwrap(), vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), ); @@ -833,7 +679,7 @@ mod tests { } #[test] - fn test_versioned_receipt() { + 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); @@ -853,66 +699,77 @@ mod tests { let mut claim_queue = BTreeMap::new(); claim_queue.insert( - new_ccr.descriptor().core_index().unwrap(), + new_ccr.descriptor.core_index().unwrap(), vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), ); let encoded_ccr = new_ccr.encode(); - let versioned_ccr = - VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); + let decoded_ccr: CommittedCandidateReceipt = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); - assert_eq!(versioned_ccr.core_index(), Some(CoreIndex(123))); + assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent()); + assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id()); - if let VersionedCommittedCandidateReceipt::V2(ref v2_receipt) = versioned_ccr { - assert_eq!(v2_receipt.check(200, &claim_queue), Ok(())); - } + 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(200, &claim_queue), Ok(())); + + assert_eq!(new_ccr.hash(), v2_ccr.hash()); + } - assert_eq!(new_ccr.hash(), versioned_ccr.to_plain().hash()); + 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") } #[test] - fn test_backward_compatible() { + 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 versioned_ccr = - VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); + let new_ccr = CommittedCandidateReceiptV2::decode(&mut encoded_ccr.as_slice()).unwrap(); - // Since collator sig and id are zeroed, it was decoded as V2. - // We expect the check to fail in such case because there will be no `SelectCore` - // commitment. + // Since collator sig and id are zeroed, it means that the descriptor uses format + // version 2. let mut claim_queue = BTreeMap::new(); claim_queue.insert( - versioned_ccr.core_index().unwrap(), - vec![2.into(), versioned_ccr.para_id(), 3.into()].into(), + new_ccr.descriptor.core_index().unwrap(), + vec![2.into(), new_ccr.descriptor.para_id(), 3.into()].into(), ); - if let VersionedCommittedCandidateReceipt::V2(ref v2_receipt) = versioned_ccr { - assert_eq!( - v2_receipt.check(200, &claim_queue), - Err(CandidateReceiptError::NoCoreSelected) - ); - } else { - panic!("Should have decoded as V2") - } + // We expect the check to fail in such case because there will be no `SelectCore` + // commitment. + assert_eq!(new_ccr.check(200, &claim_queue), Err(CandidateReceiptError::NoCoreSelected)); // Adding collator signature should make it decode as v1. - old_ccr.descriptor.signature = - CollatorSignature::from_slice(&vec![99u8; 64]).expect("64 bytes; qed"); + 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 versioned_ccr = - VersionedCommittedCandidateReceipt::decode(&mut encoded_ccr.as_slice()).unwrap(); - if let VersionedCommittedCandidateReceipt::V1(ref v1_receipt) = versioned_ccr { - assert_eq!(v1_receipt.descriptor.signature, old_ccr.descriptor.signature); - } else { - panic!("Should have decoded as V1") - } + let new_ccr = CommittedCandidateReceiptV2::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!(versioned_ccr.core_index(), None); - assert_eq!(versioned_ccr.para_id(), ParaId::new(1000)); - assert_eq!(old_ccr.hash(), versioned_ccr.to_plain().hash()); + 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 c262f16df077..cb644372f758 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,10 +23,7 @@ //! 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, - VersionedCandidateReceipt, - }, + vstaging::{CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2}, CandidateCommitments, CandidateDescriptor, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreIndex, Hash, HeadData, Id as ParaId, PersistedValidationData, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, From 2831c5e0cb2f295609a16649a38babc66d36f409 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 8 Aug 2024 23:19:06 +0300 Subject: [PATCH 05/40] Add missing primitives and fix things Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 139 ++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 8 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 03075e820524..f668b3a362bd 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,14 +15,14 @@ // along with Polkadot. If not, see . //! Staging Primitives. -use crate::ValidityAttestation; +use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here use super::{ Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CollatorId, CollatorSignature, CoreIndex, Hash, HashT, Header, Id, Id as ParaId, MultiDisputeStatementSet, UncheckedSignedAvailabilityBitfields, ValidationCodeHash, - ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, HeadData, GroupIndex, async_backing::Constraints, ScheduledCore }; use bitvec::prelude::*; use sp_application_crypto::ByteArray; @@ -158,6 +158,7 @@ pub struct CandidateDescriptorV2 { validation_code_hash: ValidationCodeHash, } + impl CandidateDescriptorV2 { /// Constructor pub fn new( @@ -208,6 +209,27 @@ pub struct CommittedCandidateReceiptV2 { pub commitments: CandidateCommitments, } + + +/// An even 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 CandidateReceiptV2 { /// Get a reference to the candidate descriptor. pub fn descriptor(&self) -> &CandidateDescriptorV2 { @@ -336,16 +358,17 @@ pub enum CandidateReceiptError { macro_rules! impl_getter { ($field:ident, $type:ident) => { - fn $field(&self) -> $type { + /// Returns the value of $field field. + pub fn $field(&self) -> $type { self.$field } }; } -impl CandidateDescriptorV2 { +impl CandidateDescriptorV2 { impl_getter!(erasure_root, Hash); impl_getter!(para_head, Hash); - impl_getter!(relay_parent, Hash); + impl_getter!(relay_parent, H); impl_getter!(para_id, ParaId); impl_getter!(persisted_validation_data_hash, Hash); impl_getter!(pov_hash, Hash); @@ -454,18 +477,17 @@ impl CommittedCandidateReceiptV2 { None } }) - .cloned() .collect::>(); if assigned_cores.is_empty() { return Err(CandidateReceiptError::NoAssignment) } - let core_index = *assigned_cores + let core_index = assigned_cores .get(core_selector.0 as usize % assigned_cores.len()) .expect("provided index is always less than queue len; qed"); - if core_index != descriptor_core_index { + if **core_index != descriptor_core_index { return Err(CandidateReceiptError::CoreIndexMismatch) } @@ -523,6 +545,11 @@ impl BackedCandidate { &self.candidate } + /// Get a reference to the descriptor of the candidate. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.candidate.descriptor + } + /// Get a reference to the validity votes of the candidate. pub fn validity_votes(&self) -> &[ValidityAttestation] { &self.validity_votes @@ -592,6 +619,102 @@ impl BackedCandidate { } } +/// 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, +} + + +/// 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, +} + +/// 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>, +} + + +/// 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, +} + + #[cfg(test)] mod tests { use super::*; From c767d60da43a8d6aad953c32752bca772f6da494 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 8 Aug 2024 23:20:06 +0300 Subject: [PATCH 06/40] Implement v2 receipts in polkadot-runtime-parachains Signed-off-by: Andrei Sandu --- .../node/test/client/src/block_builder.rs | 2 +- polkadot/runtime/parachains/src/builder.rs | 4 +-- .../parachains/src/inclusion/migration.rs | 3 +- .../runtime/parachains/src/inclusion/mod.rs | 30 +++++++++---------- .../parachains/src/paras_inherent/mod.rs | 28 ++++++++--------- .../parachains/src/runtime_api_impl/v10.rs | 13 ++++---- .../src/runtime_api_impl/vstaging.rs | 2 +- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs index 71bcdaffac4e..ed24cd55d084 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::{Block, vstaging::InherentData as ParachainsInherentData}; use polkadot_test_runtime::UncheckedExtrinsic; use polkadot_test_service::GetLastTimestamp; use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index b2a67ee8dd24..86473fafc8b0 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -31,9 +31,9 @@ use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, + vstaging::CandidateDescriptorV2 as CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + Id as ParaId, IndexedVec, vstaging::InherentData as ParachainsInherentData, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 36a810d341c6..522d224d00d2 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -20,8 +20,7 @@ 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; diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 115eee975530..75b80d06fc18 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -45,9 +45,9 @@ 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, + effective_minimum_backing_votes, supermajority_threshold, well_known_keys, vstaging::BackedCandidate, + CandidateCommitments, vstaging::CandidateDescriptorV2 as CandidateDescriptor, CandidateHash, vstaging::CandidateReceiptV2 as CandidateReceipt, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, ValidityAttestation, }; @@ -749,7 +749,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(), }; @@ -870,7 +870,7 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); weight.saturating_add(paras::Pallet::::schedule_code_upgrade( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), new_code, now, &config, @@ -880,19 +880,19 @@ impl Pallet { // enact the messaging facet of the candidate. weight.saturating_accrue(dmp::Pallet::::prune_dmq( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.processed_downward_messages, )); weight.saturating_accrue(Self::receive_upward_messages( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.upward_messages.as_slice(), )); weight.saturating_accrue(hrmp::Pallet::::prune_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), BlockNumberFor::::from(commitments.hrmp_watermark), )); weight.saturating_accrue(hrmp::Pallet::::queue_outbound_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.horizontal_messages, )); @@ -904,7 +904,7 @@ impl Pallet { )); weight.saturating_add(paras::Pallet::::note_new_head( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.head_data, relay_parent_number, )) @@ -1242,8 +1242,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) = { @@ -1263,7 +1263,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, ); } @@ -1272,12 +1272,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/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 9d27e86ef901..de616ea763b9 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -48,10 +48,10 @@ 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, + effective_minimum_backing_votes, node_features::FeatureIndex, vstaging::BackedCandidate, CandidateHash, + vstaging::CandidateReceiptV2 as CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, + DisputeStatementSet, HeadData, vstaging::InherentData as ParachainsInherentData, + MultiDisputeStatementSet, vstaging::ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, }; @@ -763,7 +763,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() @@ -965,7 +965,7 @@ fn sanitize_backed_candidates( let mut candidates_per_para: BTreeMap> = BTreeMap::new(); for candidate in backed_candidates { candidates_per_para - .entry(candidate.descriptor().para_id) + .entry(candidate.descriptor().para_id()) .or_default() .push(candidate); } @@ -984,7 +984,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 @@ -1165,13 +1165,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 }, @@ -1372,7 +1372,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/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 697890232211..f0107d6f21e2 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -27,14 +27,15 @@ 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, + vstaging::{CoreState,OccupiedCore, BackingState, CandidateEvent, ScrapedOnChainVotes, CandidatePendingAvailability, CommittedCandidateReceiptV2 as CommittedCandidateReceipt}, + slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - NodeFeatures, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, + 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..f110099d968c 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -21,7 +21,7 @@ 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 From 96999e31b96f60954bf29f4e7f2db03aa579f0a5 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:04:28 +0300 Subject: [PATCH 07/40] add missing stuff Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 176 ++++++++++++++++++++---- 1 file changed, 153 insertions(+), 23 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index f668b3a362bd..9541484d5028 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -19,16 +19,20 @@ use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here use super::{ - Balance, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, - CollatorId, CollatorSignature, CoreIndex, Hash, HashT, Header, Id, Id as ParaId, - MultiDisputeStatementSet, UncheckedSignedAvailabilityBitfields, ValidationCodeHash, - ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, HeadData, GroupIndex, async_backing::Constraints, ScheduledCore + async_backing::Constraints, Balance, BlakeTwo256, BlockNumber, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, + HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, }; use bitvec::prelude::*; use sp_application_crypto::ByteArray; -use alloc::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; -use codec::{Decode, Encode}; +use alloc::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + vec, + vec::Vec, +}; +use codec::{Decode, Encode, WrapperTypeDecode}; use scale_info::TypeInfo; use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; @@ -158,6 +162,21 @@ pub struct CandidateDescriptorV2 { 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, + } + } +} impl CandidateDescriptorV2 { /// Constructor @@ -187,6 +206,11 @@ impl CandidateDescriptorV2 { validation_code_hash, } } + + #[cfg(feature = "test")] + pub fn set_pov_hash(&mut self, pov_hash: Hash) { + self.pov_hash = pov_hash; + } } /// A candidate-receipt at version 2. @@ -209,8 +233,6 @@ pub struct CommittedCandidateReceiptV2 { pub commitments: CandidateCommitments, } - - /// An even concerning a candidate. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] @@ -230,6 +252,29 @@ pub enum CandidateEvent { CandidateTimedOut(CandidateReceiptV2, HeadData, CoreIndex), } +impl From> for super::v7::CandidateEvent { + fn from(value: CandidateEvent) -> Self { + match value { + CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) => + super::v7::CandidateEvent::CandidateBacked( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) => + super::v7::CandidateEvent::CandidateIncluded( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) => + super::v7::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), + } + } +} + impl CandidateReceiptV2 { /// Get a reference to the candidate descriptor. pub fn descriptor(&self) -> &CandidateDescriptorV2 { @@ -289,6 +334,18 @@ impl Ord for CommittedCandidateReceiptV2 { } } +impl From> for super::v7::CommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments: value.commitments } + } +} + +impl From> for super::v7::CandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } + } +} + /// A strictly increasing sequence number, tipically this would be the parachain block number. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] pub struct CoreSelector(pub BlockNumber); @@ -388,27 +445,36 @@ impl CandidateDescriptorV2 { } } + fn rebuild_collator_field(&self) -> CollatorId { + let mut collator_id = vec![self.version.0]; + let core_index: [u8; 2] = unsafe { core::mem::transmute(self.core_index) }; + let session_index: [u8; 4] = unsafe { core::mem::transmute(self.session_index) }; + + collator_id.append(&mut core_index.as_slice().to_vec()); + collator_id.append(&mut session_index.as_slice().to_vec()); + collator_id.append(&mut self.reserved25b.as_slice().to_vec()); + + CollatorId::from_slice(&collator_id.as_slice()).expect("Slice size is exactly 32 bytes") + } + /// Returns the collator id if this is a v1 `CandidateDescriptor` pub fn collator(&self) -> Option { if self.version() == CandidateDescriptorVersion::V1 { - let mut collator_id = vec![self.version.0]; - let core_index: [u8; 2] = unsafe { std::mem::transmute(self.core_index) }; - let session_index: [u8; 4] = unsafe { std::mem::transmute(self.session_index) }; - - collator_id.append(&mut core_index.as_slice().to_vec()); - collator_id.append(&mut session_index.as_slice().to_vec()); - collator_id.append(&mut self.reserved25b.as_slice().to_vec()); - - return Some(CollatorId::from_slice(&collator_id.as_slice()).ok()?) + Some(self.rebuild_collator_field()) + } else { + None } + } - None + fn rebuild_signature_field(&self) -> CollatorSignature { + CollatorSignature::from_slice(self.reserved64b.as_slice()) + .expect("Slice size is exactly 64 bytes") } /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. pub fn signature(&self) -> Option { if self.version() == CandidateDescriptorVersion::V1 { - return Some(CollatorSignature::from_slice(self.reserved64b.as_slice()).ok()?) + return Some(self.rebuild_signature_field()) } None @@ -635,6 +701,19 @@ pub struct ScrapedOnChainVotes { pub disputes: MultiDisputeStatementSet, } +impl From> for super::v7::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, + } + } +} /// A candidate pending availability. #[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] @@ -651,6 +730,20 @@ pub struct CandidatePendingAvailability { pub max_pov_size: u32, } +impl From> + for super::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)] @@ -663,7 +756,18 @@ pub struct BackingState { pub pending_availability: Vec>, } - +impl From> for super::async_backing::BackingState { + fn from(value: BackingState) -> Self { + Self { + constraints: value.constraints, + pending_availability: value + .pending_availability + .into_iter() + .map(|candidate| candidate.into()) + .collect::>(), + } + } +} /// Information about a core which is currently occupied. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] @@ -692,7 +796,6 @@ pub struct OccupiedCore { pub candidate_descriptor: CandidateDescriptorV2, } - /// The state of a particular availability core. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] @@ -714,6 +817,31 @@ pub enum CoreState { Free, } +impl From> for super::v7::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::v7::CoreState { + fn from(value: CoreState) -> Self { + match value { + CoreState::Free => super::v7::CoreState::Free, + CoreState::Scheduled(core) => super::v7::CoreState::Scheduled(core), + CoreState::Occupied(occupied_core) => + super::v7::CoreState::Occupied(occupied_core.into()), + } + } +} #[cfg(test)] mod tests { @@ -863,7 +991,8 @@ mod tests { old_ccr.descriptor.para_id = ParaId::new(1000); let encoded_ccr: Vec = old_ccr.encode(); - let new_ccr = CommittedCandidateReceiptV2::decode(&mut encoded_ccr.as_slice()).unwrap(); + 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. @@ -885,7 +1014,8 @@ mod tests { let encoded_ccr: Vec = old_ccr.encode(); - let new_ccr = CommittedCandidateReceiptV2::decode(&mut encoded_ccr.as_slice()).unwrap(); + 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)); From c5f2dc3237809d6094a3ace158ebde1f9cd1678b Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:05:18 +0300 Subject: [PATCH 08/40] Switch parachains runtime to use new primitives Signed-off-by: Andrei Sandu --- .../parachains/src/runtime_api_impl/v10.rs | 21 +++++++++++-------- .../src/runtime_api_impl/vstaging.rs | 4 +++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index f0107d6f21e2..608db7434b87 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -27,16 +27,19 @@ use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use polkadot_primitives::{ async_backing::{ - AsyncBackingParams, Constraints, - InboundHrmpLimitations, OutboundHrmpChannelLimitations, + AsyncBackingParams, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, }, - vstaging::{CoreState,OccupiedCore, BackingState, CandidateEvent, ScrapedOnChainVotes, CandidatePendingAvailability, CommittedCandidateReceiptV2 as CommittedCandidateReceipt}, - slashing, 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, + slashing, + vstaging::{ + BackingState, CandidateEvent, CandidatePendingAvailability, + 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 f110099d968c..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::{vstaging::CommittedCandidateReceiptV2 as 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 From dbb0160392fa013acbc6560e4b5737c1476d6ce6 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:05:58 +0300 Subject: [PATCH 09/40] use vstaging primitives Signed-off-by: Andrei Sandu --- polkadot/primitives/Cargo.toml | 1 + polkadot/primitives/src/runtime_api.rs | 16 ++-- polkadot/runtime/parachains/Cargo.toml | 4 +- polkadot/runtime/parachains/src/builder.rs | 49 ++++++----- .../parachains/src/inclusion/migration.rs | 10 ++- .../runtime/parachains/src/inclusion/mod.rs | 14 +-- .../runtime/parachains/src/inclusion/tests.rs | 88 ++++--------------- .../parachains/src/paras_inherent/mod.rs | 17 ++-- .../parachains/src/paras_inherent/tests.rs | 68 ++------------ 9 files changed, 95 insertions(+), 172 deletions(-) diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 09afc9662d62..311fc210d42b 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -64,3 +64,4 @@ 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..6412a67b460d 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/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index cfe373e8cba2..59af889fe6e7 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -49,7 +49,7 @@ frame-system = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } -polkadot-primitives = { workspace = true } +polkadot-primitives = { workspace = true, features=["test"] } rand = { workspace = true } rand_chacha = { workspace = true } @@ -59,6 +59,8 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] +polkadot-primitives = { workspace = true } + 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 86473fafc8b0..c1e7844de470 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,12 +30,17 @@ 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, - vstaging::CandidateDescriptorV2 as CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + InherentData as ParachainsInherentData, + }, + AvailabilityBitfield, CandidateCommitments, CandidateHash, CollatorId, CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, vstaging::InherentData as ParachainsInherentData, InvalidDisputeStatementKind, - PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, - ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, + Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, + SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, + ValidatorIndex, ValidityAttestation, }; use sp_core::{sr25519, ByteArray, H256}; use sp_runtime::{ @@ -295,17 +300,17 @@ impl BenchBuilder { } fn candidate_descriptor_mock() -> CandidateDescriptor { - CandidateDescriptor:: { - para_id: 0.into(), - 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: mock_validation_code().hash(), - } + CandidateDescriptor::::new( + 0.into(), + Default::default(), + CoreIndex(0), + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + mock_validation_code().hash(), + ) } /// Create a mock of `CandidatePendingAvailability`. @@ -631,17 +636,17 @@ impl BenchBuilder { scheduler::Pallet::::group_validators(group_idx).unwrap(); let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { + descriptor: CandidateDescriptor::::new( para_id, relay_parent, - collator: dummy_collator(), + CoreIndex(0), + 1, persisted_validation_data_hash, pov_hash, - erasure_root: Default::default(), - signature: dummy_collator_signature(), - para_head: head_data.hash(), + Default::default(), + head_data.hash(), validation_code_hash, - }, + ), commitments: CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 522d224d00d2..143c4e7d629a 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -20,7 +20,8 @@ pub mod v0 { use frame_support::{storage_alias, Twox64Concat}; use frame_system::pallet_prelude::BlockNumberFor; use polkadot_primitives::{ - vstaging::CandidateDescriptorV2 as CandidateDescriptor, AvailabilityBitfield, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex + vstaging::CandidateDescriptorV2 as CandidateDescriptor, AvailabilityBitfield, + CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex, }; use scale_info::TypeInfo; @@ -218,7 +219,8 @@ 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, dummy_candidate_descriptor_v2, + dummy_hash, }; #[test] @@ -234,7 +236,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 { @@ -284,7 +286,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 75b80d06fc18..d3edad7b3d6c 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, vstaging::BackedCandidate, - CandidateCommitments, vstaging::CandidateDescriptorV2 as CandidateDescriptor, CandidateHash, vstaging::CandidateReceiptV2 as CandidateReceipt, - vstaging::CommittedCandidateReceiptV2 as 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}; diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 3ead456cde5a..bfb2f228c452 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,21 +26,19 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, - UncheckedSignedAvailabilityBitfields, + effective_minimum_backing_votes, vstaging::CandidateDescriptorV2, AvailabilityBitfield, + SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, - 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, + BlockNumber, CandidateCommitments, CollatorId, CompactStatement as Statement, Hash, + SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, + PARACHAIN_KEY_TYPE_ID, }; +use polkadot_primitives_test_helpers::dummy_validation_code; use sc_keystore::LocalKeystore; use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; @@ -96,24 +94,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], @@ -303,17 +283,17 @@ impl std::default::Default for TestCandidateBuilder { impl TestCandidateBuilder { pub(crate) fn build(self) -> CommittedCandidateReceipt { CommittedCandidateReceipt { - descriptor: CandidateDescriptor { - para_id: self.para_id, - pov_hash: self.pov_hash, - relay_parent: self.relay_parent, - persisted_validation_data_hash: self.persisted_validation_data_hash, - 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(), - }, + descriptor: CandidateDescriptorV2::new( + self.para_id, + self.relay_parent, + CoreIndex(0), + 1, + self.persisted_validation_data_hash, + self.pov_hash, + Default::default(), + self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), + self.validation_code.hash(), + ), commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, @@ -1280,10 +1260,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, @@ -1370,7 +1346,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, @@ -1404,7 +1379,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); // Insufficient backing. let backed = back_candidate( @@ -1477,10 +1451,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, @@ -1530,10 +1500,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, @@ -1607,8 +1576,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1657,8 +1624,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1695,8 +1660,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1733,8 +1696,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1834,7 +1795,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let mut candidate_b = TestCandidateBuilder { para_id: chain_b, @@ -1845,7 +1805,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); let mut candidate_c = TestCandidateBuilder { para_id: thread_a, @@ -1856,7 +1815,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let backed_a = back_candidate( candidate_a.clone(), @@ -1977,7 +1935,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 }; @@ -2129,7 +2087,6 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let mut candidate_b_1 = TestCandidateBuilder { para_id: chain_b, @@ -2140,7 +2097,6 @@ 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 { @@ -2157,7 +2113,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(), @@ -2405,7 +2360,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(), @@ -2568,7 +2522,6 @@ 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 { @@ -2584,7 +2537,6 @@ 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 { @@ -2600,7 +2552,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( @@ -2832,7 +2783,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/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index de616ea763b9..66330674898b 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, vstaging::BackedCandidate, CandidateHash, - vstaging::CandidateReceiptV2 as CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, - DisputeStatementSet, HeadData, vstaging::InherentData as ParachainsInherentData, - MultiDisputeStatementSet, vstaging::ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, - SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, 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; diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 59fbb0948373..eae61f3c57b0 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -1381,7 +1381,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); @@ -1402,9 +1402,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); @@ -1481,9 +1481,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; @@ -1898,8 +1896,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1932,7 +1928,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(), @@ -2136,8 +2132,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2174,8 +2168,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2213,8 +2205,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2252,8 +2242,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2289,8 +2277,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -2325,8 +2311,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2360,8 +2344,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2393,8 +2375,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2426,8 +2406,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2666,8 +2644,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let prev_backed: BackedCandidate = back_candidate( candidate, @@ -2697,8 +2673,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2732,8 +2706,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); - let backed_1: BackedCandidate = back_candidate( candidate_1, &validators, @@ -2770,8 +2742,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); - let backed_2 = back_candidate( candidate_2.clone(), &validators, @@ -2801,8 +2771,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); - let backed_3 = back_candidate( candidate_3, &validators, @@ -2835,8 +2803,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2873,8 +2839,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2913,8 +2877,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed: BackedCandidate = back_candidate( candidate.clone(), &validators, @@ -3169,8 +3131,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3208,8 +3168,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3241,8 +3199,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3275,8 +3231,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3314,8 +3268,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3353,8 +3305,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3714,9 +3664,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()); } } @@ -3752,7 +3702,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()); } } From 5efab68c7d848e09fdcbffc92b1ef385148c1cf0 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:06:11 +0300 Subject: [PATCH 10/40] update rococo and westend Signed-off-by: Andrei Sandu --- polkadot/runtime/rococo/src/lib.rs | 18 +++++++++++------- polkadot/runtime/westend/src/lib.rs | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2f23b889916b..5534b86a564d 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, @@ -2025,7 +2029,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/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 838ba17e5613..67c43de8bae7 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, @@ -2085,7 +2089,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) } From c2232e425d5b2f8d0a4131963bfe4233193979d3 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:06:57 +0300 Subject: [PATCH 11/40] client keeps using the old primitives Signed-off-by: Andrei Sandu --- .../src/lib.rs | 22 ++++++++++-- polkadot/node/service/src/fake_runtime_api.rs | 16 +++++---- .../subsystem-types/src/runtime_client.rs | 34 +++++++++++++++---- .../node/test/client/src/block_builder.rs | 2 +- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index 38ba84748c1e..65c0592ff5e5 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 ed24cd55d084..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, vstaging::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}; From 87b079f82219d62e4aca3076840583fcabb3e661 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 15:30:16 +0300 Subject: [PATCH 12/40] no unsafe pls Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 9541484d5028..b1f5a8387260 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -32,7 +32,7 @@ use alloc::{ vec, vec::Vec, }; -use codec::{Decode, Encode, WrapperTypeDecode}; +use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; @@ -447,8 +447,8 @@ impl CandidateDescriptorV2 { fn rebuild_collator_field(&self) -> CollatorId { let mut collator_id = vec![self.version.0]; - let core_index: [u8; 2] = unsafe { core::mem::transmute(self.core_index) }; - let session_index: [u8; 4] = unsafe { core::mem::transmute(self.session_index) }; + let core_index: [u8; 2] = self.core_index.to_ne_bytes(); + let session_index: [u8; 4] = self.session_index.to_ne_bytes(); collator_id.append(&mut core_index.as_slice().to_vec()); collator_id.append(&mut session_index.as_slice().to_vec()); From 00e8c13133007f1e68879e626bc5e569ff368a3d Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 17:28:50 +0300 Subject: [PATCH 13/40] move async backing primtiives to own file Signed-off-by: Andrei Sandu --- polkadot/primitives/src/runtime_api.rs | 2 +- .../primitives/src/vstaging/async_backing.rs | 76 +++++++++++++++++++ polkadot/primitives/src/vstaging/mod.rs | 55 +------------- .../parachains/src/runtime_api_impl/v10.rs | 2 +- 4 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 polkadot/primitives/src/vstaging/async_backing.rs diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index 6412a67b460d..ddebe99e6214 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -264,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/vstaging/async_backing.rs b/polkadot/primitives/src/vstaging/async_backing.rs new file mode 100644 index 000000000000..bdf94e0f00db --- /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::v7::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::v7::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 b1f5a8387260..e62e8aa3d760 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -39,6 +39,8 @@ use sp_core::RuntimeDebug; use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; +pub mod async_backing; + /// Scheduler configuration parameters. All coretime/ondemand parameters are here. #[derive( RuntimeDebug, @@ -715,59 +717,6 @@ impl From> for super::v7::Scra } } -/// 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 super::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 super::async_backing::BackingState { - fn from(value: BackingState) -> Self { - Self { - constraints: value.constraints, - pending_availability: value - .pending_availability - .into_iter() - .map(|candidate| candidate.into()) - .collect::>(), - } - } -} /// Information about a core which is currently occupied. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 608db7434b87..b72b1b550a0b 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -31,7 +31,7 @@ use polkadot_primitives::{ }, slashing, vstaging::{ - BackingState, CandidateEvent, CandidatePendingAvailability, + async_backing::BackingState, CandidateEvent, async_backing::CandidatePendingAvailability, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, OccupiedCore, ScrapedOnChainVotes, }, From cd4d02f963aed56fe41969191966772be17c353d Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 18:00:54 +0300 Subject: [PATCH 14/40] fix Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 7 +++++-- polkadot/runtime/parachains/src/runtime_api_impl/v10.rs | 6 +++--- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index e62e8aa3d760..4f69073648fd 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -39,6 +39,7 @@ use sp_core::RuntimeDebug; use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; +/// Async backing primitives pub mod async_backing; /// Scheduler configuration parameters. All coretime/ondemand parameters are here. @@ -209,6 +210,7 @@ impl CandidateDescriptorV2 { } } + /// 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; @@ -456,7 +458,8 @@ impl CandidateDescriptorV2 { collator_id.append(&mut session_index.as_slice().to_vec()); collator_id.append(&mut self.reserved25b.as_slice().to_vec()); - CollatorId::from_slice(&collator_id.as_slice()).expect("Slice size is exactly 32 bytes") + 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` @@ -470,7 +473,7 @@ impl CandidateDescriptorV2 { fn rebuild_signature_field(&self) -> CollatorSignature { CollatorSignature::from_slice(self.reserved64b.as_slice()) - .expect("Slice size is exactly 64 bytes") + .expect("Slice size is exactly 64 bytes; qed") } /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index b72b1b550a0b..ead825b38f07 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -31,9 +31,9 @@ use polkadot_primitives::{ }, slashing, vstaging::{ - async_backing::BackingState, CandidateEvent, async_backing::CandidatePendingAvailability, - CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, OccupiedCore, - ScrapedOnChainVotes, + async_backing::{BackingState, CandidatePendingAvailability}, + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + OccupiedCore, ScrapedOnChainVotes, }, ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 5534b86a564d..688fe6317cc4 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2029,7 +2029,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/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 67c43de8bae7..6f487960b810 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2089,7 +2089,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) } From 5509e3340810973ea4344466bebb7e2cb5953574 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 18:33:04 +0300 Subject: [PATCH 15/40] fix test build Signed-off-by: Andrei Sandu --- polkadot/runtime/parachains/src/builder.rs | 22 +++------ .../parachains/src/inclusion/migration.rs | 3 +- .../runtime/parachains/src/inclusion/tests.rs | 44 ++++++++--------- .../parachains/src/paras_inherent/tests.rs | 49 +++++++++---------- 4 files changed, 53 insertions(+), 65 deletions(-) diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index c1e7844de470..ebc956a08e67 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -36,29 +36,19 @@ use polkadot_primitives::{ CommittedCandidateReceiptV2 as CommittedCandidateReceipt, InherentData as ParachainsInherentData, }, - AvailabilityBitfield, CandidateCommitments, CandidateHash, CollatorId, CollatorSignature, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, - SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, - ValidatorIndex, ValidityAttestation, + AvailabilityBitfield, CandidateCommitments, CandidateHash, 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::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") -} - -/// Create a null collator signature. -pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") -} - fn mock_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3]) } diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 143c4e7d629a..2a215d5d595c 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -219,8 +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_candidate_descriptor_v2, - dummy_hash, + dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash, }; #[test] diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index bfb2f228c452..2125dffe2592 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -1223,7 +1223,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), @@ -1232,7 +1232,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), @@ -1244,7 +1244,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), @@ -1331,7 +1331,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), @@ -1370,7 +1370,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), @@ -1432,7 +1432,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), @@ -1441,7 +1441,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), @@ -1565,7 +1565,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), @@ -1614,7 +1614,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), @@ -1649,7 +1649,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), @@ -1685,7 +1685,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), @@ -1786,7 +1786,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), @@ -1796,7 +1796,7 @@ fn backing_works() { } .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), @@ -1806,7 +1806,7 @@ fn backing_works() { } .build(); - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -2078,7 +2078,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), @@ -2088,7 +2088,7 @@ fn backing_works_with_elastic_scaling_mvp() { } .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), @@ -2099,7 +2099,7 @@ fn backing_works_with_elastic_scaling_mvp() { .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), @@ -2350,7 +2350,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), @@ -2509,7 +2509,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), @@ -2524,7 +2524,7 @@ fn check_allowed_relay_parents() { .build(); 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), @@ -2539,7 +2539,7 @@ fn check_allowed_relay_parents() { .build(); 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), @@ -2773,7 +2773,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), diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index eae61f3c57b0..6c82233e0820 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -1495,7 +1495,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; @@ -1879,7 +1878,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), @@ -2114,7 +2113,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), @@ -2150,7 +2149,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), @@ -2188,7 +2187,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), @@ -2225,7 +2224,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), @@ -2260,7 +2259,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), @@ -2293,7 +2292,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), @@ -2327,7 +2326,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), @@ -2358,7 +2357,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), @@ -2389,7 +2388,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), @@ -2626,7 +2625,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), @@ -2655,7 +2654,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), @@ -2688,7 +2687,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), @@ -2724,7 +2723,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), @@ -2753,7 +2752,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), @@ -2785,7 +2784,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), @@ -2821,7 +2820,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), @@ -2859,7 +2858,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), @@ -3113,7 +3112,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), @@ -3149,7 +3148,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), @@ -3180,7 +3179,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), @@ -3213,7 +3212,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), @@ -3249,7 +3248,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), @@ -3286,7 +3285,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), From f8b86d26c687a447120122277966dd4322f4b06f Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 12 Aug 2024 23:03:31 +0300 Subject: [PATCH 16/40] fix test-runtime Signed-off-by: Andrei Sandu --- polkadot/runtime/test-runtime/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8e34320d38f2..26ddf8c90baf 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -59,10 +59,10 @@ 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, + slashing, AccountId, AccountIndex, Balance, BlockNumber, vstaging::CandidateEvent, CandidateHash, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, vstaging::CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, vstaging::ScrapedOnChainVotes, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -978,7 +978,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) } From fe2fbfb6c3b759cfaed13f1ed50425cb4bd14827 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 11:07:39 +0300 Subject: [PATCH 17/40] self review feedback Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 38 ++++++++++++++---------- polkadot/runtime/test-runtime/src/lib.rs | 12 +++++--- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 4f69073648fd..74d75fcc46eb 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -350,9 +350,10 @@ impl From> for super::v7::CandidateReceipt { } } -/// A strictly increasing sequence number, tipically this would be the parachain block number. +/// A strictly increasing sequence number, tipically this would be the least significant byte of the +/// block number. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub struct CoreSelector(pub BlockNumber); +pub struct CoreSelector(pub u8); /// An offset in the relay chain claim queue. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] @@ -375,7 +376,7 @@ pub const UMP_SEPARATOR: Vec = vec![]; impl CandidateCommitments { /// Returns the core selector and claim queue offset the candidate has commited to, if any. pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { - // We need at least 2 messages for the separator and core index + // We need at least 2 messages for the separator and core selector if self.upward_messages.len() < 2 { return None } @@ -388,7 +389,7 @@ impl CandidateCommitments { .take_while(|message| message != &UMP_SEPARATOR) .collect::>(); - // We didn't find the separator, no core index commitment. + // Check for UMP separator if upward_commitments.len() == self.upward_messages.len() || upward_commitments.is_empty() { return None } @@ -510,32 +511,43 @@ impl CommittedCandidateReceiptV2 { /// Returns error if: /// - descriptor core index is different than the core selected /// by the commitments - /// - the core index is out of bounds wrt `n_cores`. + /// - the core index is out of bounds pub fn check( &self, n_cores: u32, // TODO: consider promoting `ClaimQueueSnapshot` as primitive claim_queue: &BTreeMap>, ) -> Result<(), CandidateReceiptError> { + // Don't check v1 descriptors. + if self.descriptor.version() == CandidateDescriptorVersion::V1 { + return Ok(()) + } + if claim_queue.is_empty() { return Err(CandidateReceiptError::NoAssignment) } + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + if descriptor_core_index.0 > n_cores - 1 { + return Err(CandidateReceiptError::InvalidCoreIndex) + } + + let (core_selector, cq_offset) = + self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; + + // TODO: double check the invariant of claim queue len == scheduling lookahead. + // What happens when on-demand is used and there is only 1 claim on a core. let claim_queue_depth = claim_queue .first_key_value() .ok_or(CandidateReceiptError::NoAssignment)? .1 .len(); - - 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 para_id = self.descriptor.para_id; - if cq_offset.0 as usize >= claim_queue_depth { return Err(CandidateReceiptError::InvalidSelectedCore) } + let para_id = self.descriptor.para_id; + // Get a vec of the core indices the parachain is assigned to at `cq_offset`. let assigned_cores = claim_queue .iter() @@ -562,10 +574,6 @@ impl CommittedCandidateReceiptV2 { return Err(CandidateReceiptError::CoreIndexMismatch) } - if descriptor_core_index.0 > n_cores - 1 { - return Err(CandidateReceiptError::InvalidCoreIndex) - } - Ok(()) } } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 26ddf8c90baf..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, vstaging::CandidateEvent, CandidateHash, - vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, vstaging::CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, vstaging::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, }; From 975e13bafcbaf2c27c5ac5bcb2dafe5f897ca421 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 13:22:27 +0300 Subject: [PATCH 18/40] review feedback Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 9 +++++---- polkadot/runtime/parachains/Cargo.toml | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 74d75fcc46eb..7f438be9434d 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -451,13 +451,14 @@ impl CandidateDescriptorV2 { } fn rebuild_collator_field(&self) -> CollatorId { - let mut collator_id = vec![self.version.0]; + 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.append(&mut core_index.as_slice().to_vec()); - collator_id.append(&mut session_index.as_slice().to_vec()); - collator_id.append(&mut self.reserved25b.as_slice().to_vec()); + 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.reserved25b.as_slice()); CollatorId::from_slice(&collator_id.as_slice()) .expect("Slice size is exactly 32 bytes; qed") diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index 59af889fe6e7..f3123fa554ca 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -49,7 +49,7 @@ frame-system = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } -polkadot-primitives = { workspace = true, features=["test"] } +polkadot-primitives = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } @@ -59,7 +59,7 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] -polkadot-primitives = { workspace = true } +polkadot-primitives = { workspace = true, features=["test"] } futures = { workspace = true } hex-literal = { workspace = true, default-features = true } From 1c7ac55b5dce45a2f0a63e55381e0e18d58ac5e1 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 13:39:40 +0300 Subject: [PATCH 19/40] feedback Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 7f438be9434d..8251a32c4aa7 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -122,14 +122,13 @@ impl> Default for SchedulerParams #[cfg_attr(feature = "std", derive(Hash))] pub struct InternalVersion(pub u8); -/// A type representing the version of the candidate descriptor and internal version number. -/// For `V1`` the internal version number stores the first byte of the `CollatorId`. +/// 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, - /// Introduced with `CandidateDescriptorV2` + /// The new `CandidateDescriptorV2`. V2, } @@ -141,7 +140,8 @@ pub struct CandidateDescriptorV2 { para_id: ParaId, /// The hash of the relay-chain block this is executed in the context of. relay_parent: H, - /// Version field + /// Version field. The raw value here is not exposed, instead it is used + /// to determine the `CandidateDescriptorVersion`, see `fn version()` version: InternalVersion, /// The core index where the candidate is backed. core_index: u16, @@ -237,7 +237,7 @@ pub struct CommittedCandidateReceiptV2 { pub commitments: CandidateCommitments, } -/// 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 { @@ -350,7 +350,7 @@ impl From> for super::v7::CandidateReceipt { } } -/// A strictly increasing sequence number, tipically this would be the least significant byte of the +/// 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); @@ -374,7 +374,7 @@ pub enum UMPSignal { pub const UMP_SEPARATOR: Vec = vec![]; impl CandidateCommitments { - /// Returns the core selector and claim queue offset the candidate has commited to, if any. + /// 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 { From 653873b15819df3cdedb92161cfb19bbbf814420 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 13:51:08 +0300 Subject: [PATCH 20/40] feedback Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 8251a32c4aa7..56e4dcb7054a 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -381,21 +381,11 @@ impl CandidateCommitments { return None } - let upward_commitments = self - .upward_messages - .iter() - .cloned() - .rev() - .take_while(|message| message != &UMP_SEPARATOR) - .collect::>(); - - // Check for UMP separator - if upward_commitments.len() == self.upward_messages.len() || upward_commitments.is_empty() { - return None - } + let separator_pos = + self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; // Use first commitment - let Some(message) = upward_commitments.into_iter().rev().next() else { return None }; + 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)), From dc98149924a59162fda79f93eae2836fb545b502 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 13:56:51 +0300 Subject: [PATCH 21/40] clippy Signed-off-by: Andrei Sandu --- .../runtime/parachains/src/paras_inherent/benchmarking.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index c5284ba1dd1f..07bdf3d5489c 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -141,8 +141,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); } @@ -198,8 +198,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, From 0a6bce3404f3ff91e67ccabf0356c60e19321e4b Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 13 Aug 2024 23:08:03 +0300 Subject: [PATCH 22/40] chores Signed-off-by: Andrei Sandu --- polkadot/primitives/src/v7/mod.rs | 2 +- polkadot/primitives/src/vstaging/mod.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v7/mod.rs index 4b7abdb311f5..7d5d6e093c8f 100644 --- a/polkadot/primitives/src/v7/mod.rs +++ b/polkadot/primitives/src/v7/mod.rs @@ -1153,7 +1153,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 { diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 56e4dcb7054a..b089fb422f3c 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -539,13 +539,11 @@ impl CommittedCandidateReceiptV2 { let para_id = self.descriptor.para_id; - // Get a vec of the core indices the parachain is assigned to at `cq_offset`. + // Get a sorted vec of the core indices the parachain is assigned to at `cq_offset`. let assigned_cores = claim_queue .iter() .filter_map(|(core_index, queue)| { - let queued_para = queue.get(cq_offset.0 as usize)?; - - if queued_para == ¶_id { + if queue.get(cq_offset.0 as usize)? == ¶_id { Some(core_index) } else { None From 429694215f8ffba2ac3e8639da79df3ec3e32887 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 20 Aug 2024 00:58:10 +0300 Subject: [PATCH 23/40] simplify check() Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 86 ++++--------------------- 1 file changed, 12 insertions(+), 74 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index b089fb422f3c..e2dcf124b545 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -27,11 +27,7 @@ use super::{ use bitvec::prelude::*; use sp_application_crypto::ByteArray; -use alloc::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - vec, - vec::Vec, -}; +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_arithmetic::Perbill; @@ -497,69 +493,29 @@ impl CandidateDescriptorV2 { } impl CommittedCandidateReceiptV2 { - /// Performs a sanity check of the descriptor and commitment. - /// - /// Returns error if: - /// - descriptor core index is different than the core selected - /// by the commitments - /// - the core index is out of bounds - pub fn check( - &self, - n_cores: u32, - // TODO: consider promoting `ClaimQueueSnapshot` as primitive - claim_queue: &BTreeMap>, - ) -> Result<(), CandidateReceiptError> { + /// 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: &Vec) -> Result<(), CandidateReceiptError> { // Don't check v1 descriptors. if self.descriptor.version() == CandidateDescriptorVersion::V1 { return Ok(()) } - if claim_queue.is_empty() { + if assigned_cores.is_empty() { return Err(CandidateReceiptError::NoAssignment) } let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); - if descriptor_core_index.0 > n_cores - 1 { - return Err(CandidateReceiptError::InvalidCoreIndex) - } - let (core_selector, cq_offset) = + let (core_selector, _cq_offset) = self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; - // TODO: double check the invariant of claim queue len == scheduling lookahead. - // What happens when on-demand is used and there is only 1 claim on a core. - let claim_queue_depth = claim_queue - .first_key_value() - .ok_or(CandidateReceiptError::NoAssignment)? - .1 - .len(); - if cq_offset.0 as usize >= claim_queue_depth { - return Err(CandidateReceiptError::InvalidSelectedCore) - } - - let para_id = self.descriptor.para_id; - - // Get a sorted vec of the core indices the parachain is assigned to at `cq_offset`. - let assigned_cores = claim_queue - .iter() - .filter_map(|(core_index, queue)| { - if queue.get(cq_offset.0 as usize)? == ¶_id { - Some(core_index) - } else { - None - } - }) - .collect::>(); - - if assigned_cores.is_empty() { - return Err(CandidateReceiptError::NoAssignment) - } - let core_index = assigned_cores .get(core_selector.0 as usize % assigned_cores.len()) - .expect("provided index is always less than queue len; qed"); + .ok_or(CandidateReceiptError::InvalidCoreIndex)?; - if **core_index != descriptor_core_index { + if *core_index != descriptor_core_index { return Err(CandidateReceiptError::CoreIndexMismatch) } @@ -869,13 +825,7 @@ mod tests { .upward_messages .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); - let mut claim_queue = BTreeMap::new(); - claim_queue.insert( - new_ccr.descriptor.core_index().unwrap(), - vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), - ); - - assert_eq!(new_ccr.check(200, &claim_queue), Ok(())); + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); } #[test] @@ -897,12 +847,6 @@ mod tests { .upward_messages .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); - let mut claim_queue = BTreeMap::new(); - claim_queue.insert( - new_ccr.descriptor.core_index().unwrap(), - vec![2.into(), new_ccr.descriptor.para_id, 3.into()].into(), - ); - let encoded_ccr = new_ccr.encode(); let decoded_ccr: CommittedCandidateReceipt = Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); @@ -918,7 +862,7 @@ mod tests { Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); - assert_eq!(new_ccr.check(200, &claim_queue), Ok(())); + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); assert_eq!(new_ccr.hash(), v2_ccr.hash()); } @@ -945,15 +889,9 @@ mod tests { // Since collator sig and id are zeroed, it means that the descriptor uses format // version 2. - let mut claim_queue = BTreeMap::new(); - claim_queue.insert( - new_ccr.descriptor.core_index().unwrap(), - vec![2.into(), new_ccr.descriptor.para_id(), 3.into()].into(), - ); - // We expect the check to fail in such case because there will be no `SelectCore` // commitment. - assert_eq!(new_ccr.check(200, &claim_queue), Err(CandidateReceiptError::NoCoreSelected)); + 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(); From 6fb7790eb1aa7aa88acb7f355149f3f70f25b19b Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 20 Aug 2024 18:02:12 +0300 Subject: [PATCH 24/40] impl Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index e2dcf124b545..fe828c02142e 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -492,11 +492,11 @@ impl CandidateDescriptorV2 { } } -impl CommittedCandidateReceiptV2 { +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: &Vec) -> Result<(), CandidateReceiptError> { + pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> { // Don't check v1 descriptors. if self.descriptor.version() == CandidateDescriptorVersion::V1 { return Ok(()) From d0b3961e3c1ab98d9d5bb4506a6af5e9273a1e2c Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 20 Aug 2024 19:19:28 +0300 Subject: [PATCH 25/40] comment Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index fe828c02142e..8503fab2ba70 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -137,7 +137,9 @@ pub struct CandidateDescriptorV2 { /// 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()` + /// 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, From 66f7a96d5f6eddb402873b513b4a8ab8b3d57c70 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 02:21:16 +0300 Subject: [PATCH 26/40] add some tests Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 8503fab2ba70..aa47d9cbc3e2 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -808,6 +808,18 @@ mod tests { assert_eq!(old_ccr.hash(), new_ccr.hash()); } + #[test] + fn is_invalid_version_decodes_as_v1() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + 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::V1); + } + #[test] fn test_ump_commitment() { let mut new_ccr = dummy_committed_candidate_receipt_v2(); @@ -830,6 +842,34 @@ mod tests { 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); + + // 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)); + + 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(); From 5c0c919114afbc0062e7c2f76af1a47a91faba92 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 02:43:46 +0300 Subject: [PATCH 27/40] update Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 48 +++++++++++++++++-------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index aa47d9cbc3e2..623b0a1a3044 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -761,6 +761,16 @@ mod tests { 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 reserved64b = [0; 64]; @@ -811,6 +821,7 @@ mod tests { #[test] fn is_invalid_version_decodes_as_v1() { let mut new_ccr = dummy_committed_candidate_receipt_v2(); + // Put some unknown version. new_ccr.descriptor.version = InternalVersion(100); // Deserialize as V1. @@ -848,17 +859,35 @@ mod tests { new_ccr.descriptor.core_index = 0; new_ccr.descriptor.para_id = ParaId::new(1000); - // separator 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)); + 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 @@ -867,7 +896,6 @@ mod tests { // Duplicate doesn't override first signal. assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(())); - } #[test] @@ -898,7 +926,7 @@ mod tests { assert_eq!(new_ccr.hash(), decoded_ccr.hash()); - // // // Encode v1 and decode as V2 + // Encode v1 and decode as V2 let encoded_ccr = new_ccr.encode(); let v2_ccr: CommittedCandidateReceiptV2 = Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); @@ -909,16 +937,6 @@ mod tests { assert_eq!(new_ccr.hash(), v2_ccr.hash()); } - 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") - } - #[test] fn test_core_select_is_mandatory() { // Testing edge case when collators provide zeroed signature and collator id. From 38ce5899bf7c58a02183b0fb227db514609d0d02 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 16:34:56 +0300 Subject: [PATCH 28/40] prdoc Signed-off-by: Andrei Sandu --- prdoc/pr_5322.prdoc | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 prdoc/pr_5322.prdoc diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc new file mode 100644 index 000000000000..0e262f18bf92 --- /dev/null +++ b/prdoc/pr_5322.prdoc @@ -0,0 +1,33 @@ +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 + + + From 9f1d611ee48726993d71b17a473a5ce835e4584e Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 17:04:55 +0300 Subject: [PATCH 29/40] can't be happy if CI is sad Signed-off-by: Andrei Sandu --- polkadot/primitives/Cargo.toml | 1 + polkadot/primitives/src/vstaging/mod.rs | 2 +- polkadot/runtime/parachains/Cargo.toml | 2 +- prdoc/pr_5322.prdoc | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 311fc210d42b..a8cd6cb5f4e0 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -58,6 +58,7 @@ std = [ "sp-keystore?/std", "sp-runtime/std", "sp-staking/std", + "sp-std/std", ] runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 623b0a1a3044..61a3ddb408b6 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -312,7 +312,7 @@ impl CommittedCandidateReceiptV2 { self.to_plain().hash() } - /// Does this committed candidate receipt corresponds to the given [`CandidateReceipt`]? + /// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]? pub fn corresponds_to(&self, receipt: &CandidateReceiptV2) -> bool where H: PartialEq, diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index f3123fa554ca..a3eec3f9d961 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -59,7 +59,7 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] -polkadot-primitives = { workspace = true, features=["test"] } +polkadot-primitives = { workspace = true, features = ["test"] } futures = { workspace = true } hex-literal = { workspace = true, default-features = true } diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc index 0e262f18bf92..68991ad02c93 100644 --- a/prdoc/pr_5322.prdoc +++ b/prdoc/pr_5322.prdoc @@ -1,4 +1,4 @@ -title: Elastic scaling: introduce new candidate receipt primitive +title: Elastic scaling - introduce new candidate receipt primitive doc: - audience: [Runtime Dev, Node Dev] From 663817dd49bcba63772544b07afbddf79ccf1c54 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 17:08:29 +0300 Subject: [PATCH 30/40] remove newlines Signed-off-by: Andrei Sandu --- prdoc/pr_5322.prdoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc index 68991ad02c93..b4cf261f33a4 100644 --- a/prdoc/pr_5322.prdoc +++ b/prdoc/pr_5322.prdoc @@ -28,6 +28,3 @@ crates: bump: major - name: cumulus-relay-chain-inprocess-interface bump: patch - - - From a1dacc1eb24a28c06c67ef8189b202df08717ff0 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 17:10:28 +0300 Subject: [PATCH 31/40] match rfc 103 reserved field naming Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 61a3ddb408b6..af2e17d17ddf 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -146,7 +146,7 @@ pub struct CandidateDescriptorV2 { /// The session index of the candidate relay parent. session_index: SessionIndex, /// Reserved bytes. - reserved25b: [u8; 25], + 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. @@ -156,7 +156,7 @@ pub struct CandidateDescriptorV2 { /// The root of a block's erasure encoding Merkle tree. erasure_root: Hash, /// Reserved bytes. - reserved64b: [u8; 64], + 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. @@ -198,11 +198,11 @@ impl CandidateDescriptorV2 { version: InternalVersion(0), core_index: core_index.0 as u16, session_index, - reserved25b: [0; 25], + reserved1: [0; 25], persisted_validation_data_hash, pov_hash, erasure_root, - reserved64b: [0; 64], + reserved2: [0; 64], para_head, validation_code_hash, } @@ -428,7 +428,7 @@ impl CandidateDescriptorV2 { /// 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.reserved64b != [0u8; 64] || self.reserved25b != [0u8; 25] { + if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] { return CandidateDescriptorVersion::V1 } @@ -446,7 +446,7 @@ impl CandidateDescriptorV2 { 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.reserved25b.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") @@ -462,7 +462,7 @@ impl CandidateDescriptorV2 { } fn rebuild_signature_field(&self) -> CollatorSignature { - CollatorSignature::from_slice(self.reserved64b.as_slice()) + CollatorSignature::from_slice(self.reserved2.as_slice()) .expect("Slice size is exactly 64 bytes; qed") } @@ -773,7 +773,7 @@ mod tests { pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { let zeros = Hash::zero(); - let reserved64b = [0; 64]; + let reserved2 = [0; 64]; CommittedCandidateReceiptV2 { descriptor: CandidateDescriptorV2 { @@ -782,11 +782,11 @@ mod tests { version: InternalVersion(0), core_index: 123, session_index: 1, - reserved25b: Default::default(), + reserved1: Default::default(), persisted_validation_data_hash: zeros, pov_hash: zeros, erasure_root: zeros, - reserved64b, + reserved2, para_head: zeros, validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), }, From 33b80ea99d4e53d9dd897a90fa3a6e373379825f Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Wed, 21 Aug 2024 17:14:39 +0300 Subject: [PATCH 32/40] remove default cq offset Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index af2e17d17ddf..962c9a467fe7 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -357,9 +357,6 @@ pub struct CoreSelector(pub u8); #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] pub struct ClaimQueueOffset(pub u8); -/// Default claim queue offset -pub const DEFAULT_CLAIM_QUEUE_OFFSET: ClaimQueueOffset = ClaimQueueOffset(1); - /// 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 { From 1db5eb0e1c1c8ec45a320f4cc510543e4129efad Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:23:49 +0300 Subject: [PATCH 33/40] Para Inherent: filter v2 candidate descriptors (#5362) On top of https://github.com/paritytech/polkadot-sdk/pull/5322 This is an additional check to prevent nodes from passing the v2 candidate descriptors in the runtime until it is enabled via the node feature. TODO: - [ ] PRDoc --------- Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 20 +++- polkadot/primitives/test-helpers/src/lib.rs | 10 +- polkadot/runtime/parachains/src/builder.rs | 87 ++++++++++---- .../runtime/parachains/src/inclusion/tests.rs | 38 +++--- .../parachains/src/paras_inherent/mod.rs | 35 +++++- .../parachains/src/paras_inherent/tests.rs | 108 +++++++++++++++++- 6 files changed, 243 insertions(+), 55 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 962c9a467fe7..8d97ddb57fc5 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -34,7 +34,6 @@ use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; - /// Async backing primitives pub mod async_backing; @@ -179,6 +178,13 @@ impl From> for CandidateDescriptor { } } +#[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( @@ -213,6 +219,12 @@ impl CandidateDescriptorV2 { 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. @@ -573,6 +585,12 @@ impl BackedCandidate { &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 diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index cb644372f758..b0f78717dd97 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -170,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()) diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index ebc956a08e67..35835793ce9d 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -32,27 +32,38 @@ use frame_system::pallet_prelude::*; use polkadot_primitives::{ node_features::FeatureIndex, vstaging::{ - BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + BackedCandidate, CandidateDescriptorV2, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, InherentData as ParachainsInherentData, }, - AvailabilityBitfield, CandidateCommitments, CandidateHash, CompactStatement, CoreIndex, - DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, Id as ParaId, IndexedVec, - InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, - UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, - ValidityAttestation, + 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::H256; +use sp_core::{ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; - fn mock_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3]) } +// 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") +} + +// 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. /// /// This is directly from frame-benchmarking. Copy/pasted so we can use it when not compiling with @@ -116,6 +127,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, } @@ -147,6 +160,7 @@ impl BenchBuilder { code_upgrade: None, fill_claimqueue: true, unavailable_cores: vec![], + candidate_descriptor_v2: false, _phantom: core::marker::PhantomData::, } } @@ -254,6 +268,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()) @@ -289,18 +309,20 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptor { - CandidateDescriptor::::new( - 0.into(), - Default::default(), - CoreIndex(0), - 1, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - mock_validation_code().hash(), - ) + fn candidate_descriptor_mock() -> CandidateDescriptorV2 { + // Use a v1 descriptor. + CandidateDescriptor:: { + para_id: 0.into(), + relay_parent: Default::default(), + collator: junk_collator(), + persisted_validation_data_hash: Default::default(), + pov_hash: Default::default(), + erasure_root: Default::default(), + signature: junk_collator_signature(), + para_head: Default::default(), + validation_code_hash: mock_validation_code().hash(), + } + .into() } /// Create a mock of `CandidatePendingAvailability`. @@ -625,18 +647,35 @@ impl BenchBuilder { let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor::::new( + let descriptor = if self.candidate_descriptor_v2 { + CandidateDescriptorV2::new( para_id, relay_parent, - CoreIndex(0), + core_idx, 1, persisted_validation_data_hash, pov_hash, Default::default(), head_data.hash(), validation_code_hash, - ), + ) + } else { + CandidateDescriptor:: { + para_id, + relay_parent, + collator: junk_collator(), + persisted_validation_data_hash, + pov_hash, + erasure_root: Default::default(), + 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/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 2125dffe2592..f17b8ed2e0e9 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,7 +26,7 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, vstaging::CandidateDescriptorV2, AvailabilityBitfield, + effective_minimum_backing_votes, AvailabilityBitfield, CandidateDescriptor, SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; @@ -34,12 +34,13 @@ use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CollatorId, CompactStatement as Statement, Hash, - SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, - PARACHAIN_KEY_TYPE_ID, + BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, + CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, + ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; 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; @@ -283,17 +284,24 @@ impl std::default::Default for TestCandidateBuilder { impl TestCandidateBuilder { pub(crate) fn build(self) -> CommittedCandidateReceipt { CommittedCandidateReceipt { - descriptor: CandidateDescriptorV2::new( - self.para_id, - self.relay_parent, - CoreIndex(0), - 1, - self.persisted_validation_data_hash, - self.pov_hash, - Default::default(), - self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), - self.validation_code.hash(), - ), + descriptor: CandidateDescriptor { + para_id: self.para_id, + pov_hash: self.pov_hash, + relay_parent: self.relay_parent, + persisted_validation_data_hash: self.persisted_validation_data_hash, + 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: 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, diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 66330674898b..2bcc2d873131 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -51,7 +51,7 @@ use polkadot_primitives::{ effective_minimum_backing_votes, node_features::FeatureIndex, vstaging::{ - BackedCandidate, CandidateReceiptV2 as CandidateReceipt, + BackedCandidate, CandidateDescriptorVersion, CandidateReceiptV2 as CandidateReceipt, InherentData as ParachainsInherentData, ScrapedOnChainVotes, }, CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, @@ -575,6 +575,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; @@ -591,6 +597,7 @@ impl Pallet { concluded_invalid_hashes, eligible, core_index_enabled, + allow_v2_receipts, ); let count = count_backed_candidates(&backed_candidates_with_core); @@ -947,14 +954,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. @@ -964,11 +972,26 @@ 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()) .or_default() diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 6c82233e0820..c0e1636569a8 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -58,7 +58,10 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{vstaging::SchedulerParams, AvailabilityBitfield, UncheckedSigned}; + use polkadot_primitives::{ + vstaging::{InternalVersion, SchedulerParams}, + AvailabilityBitfield, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -70,6 +73,7 @@ mod enter { fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, + v2_descriptor: bool, } fn make_inherent_data( @@ -82,6 +86,7 @@ mod enter { fill_claimqueue, elastic_paras, unavailable_cores, + v2_descriptor, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -99,7 +104,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 +151,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 +247,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 +352,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 +609,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 +683,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 +755,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 +843,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 +931,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1004,6 +1018,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1111,6 +1126,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1179,6 +1195,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1245,6 +1262,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1348,6 +1366,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let mut para_inherent_data = scenario.data.clone(); @@ -1437,6 +1456,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1465,6 +1485,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 { @@ -3376,7 +3466,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3400,7 +3491,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3425,6 +3517,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3457,6 +3550,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3497,6 +3591,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3567,6 +3662,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3605,6 +3701,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); assert!(sanitized_backed_candidates.is_empty()); @@ -3641,6 +3738,7 @@ mod sanitizers { set, scheduled, core_index_enabled, + false, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); @@ -3678,6 +3776,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // We'll be left with candidates from paraid 2 and 4. @@ -3714,6 +3813,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // Only the second candidate of paraid 1 should be removed. From 984e8e1c97cf2c8238c6a6ea6b481211db543217 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 29 Aug 2024 11:49:06 +0300 Subject: [PATCH 34/40] add unknown version Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 962c9a467fe7..ccb4c132b0ae 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -126,6 +126,8 @@ pub enum CandidateDescriptorVersion { V1, /// The new `CandidateDescriptorV2`. V2, + /// An unknown version. + Unknown, } /// A unique descriptor of the candidate receipt. @@ -431,7 +433,7 @@ impl CandidateDescriptorV2 { match self.version.0 { 0 => CandidateDescriptorVersion::V2, - _ => CandidateDescriptorVersion::V1, + _ => CandidateDescriptorVersion::Unknown, } } @@ -816,8 +818,9 @@ mod tests { } #[test] - fn is_invalid_version_decodes_as_v1() { + 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); @@ -825,7 +828,7 @@ mod tests { let new_ccr: CommittedCandidateReceiptV2 = Decode::decode(&mut new_ccr.encode().as_slice()).unwrap(); - assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V1); + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); } #[test] From fab215daebc0d1800bb387548c5306d4e54796fb Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 29 Aug 2024 11:56:24 +0300 Subject: [PATCH 35/40] add check for unknown version and test Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index ccb4c132b0ae..1838d94e53b8 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -114,7 +114,7 @@ impl> Default for SchedulerParams } /// A type representing the version of the candidate descriptor and internal version number. -#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug)] +#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)] #[cfg_attr(feature = "std", derive(Hash))] pub struct InternalVersion(pub u8); @@ -403,6 +403,8 @@ pub enum CandidateReceiptError { NoAssignment, /// No core was selected. NoCoreSelected, + /// Unknown version. + UnknownVersion(InternalVersion), } macro_rules! impl_getter { @@ -503,6 +505,10 @@ impl CommittedCandidateReceiptV2 { return Ok(()) } + if self.descriptor.version() == CandidateDescriptorVersion::Unknown { + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)) + } + if assigned_cores.is_empty() { return Err(CandidateReceiptError::NoAssignment) } @@ -829,6 +835,10 @@ mod tests { 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] From 9bbe2cc788d28d993c73e52b60a4a8c417e10db1 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 29 Aug 2024 15:20:41 +0300 Subject: [PATCH 36/40] typo Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index c01c792c6d89..d154ba11af28 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -407,7 +407,7 @@ impl CandidateCommitments { pub enum CandidateReceiptError { /// The specified core index is invalid. InvalidCoreIndex, - /// The core index in commitments doesnt match the one in descriptor + /// The core index in commitments doesn't match the one in descriptor CoreIndexMismatch, /// The core selector or claim queue offset is invalid. InvalidSelectedCore, From f8ef4ce2486aa32600faab709c2da7bfb6a17e19 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Fri, 30 Aug 2024 15:56:01 +0300 Subject: [PATCH 37/40] fix merge damage Signed-off-by: Andrei Sandu --- .../primitives/src/vstaging/async_backing.rs | 4 +-- polkadot/primitives/src/vstaging/mod.rs | 26 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/polkadot/primitives/src/vstaging/async_backing.rs b/polkadot/primitives/src/vstaging/async_backing.rs index bdf94e0f00db..8706214b5a01 100644 --- a/polkadot/primitives/src/vstaging/async_backing.rs +++ b/polkadot/primitives/src/vstaging/async_backing.rs @@ -37,7 +37,7 @@ pub struct CandidatePendingAvailability { } impl From> - for crate::v7::async_backing::CandidatePendingAvailability + for crate::v8::async_backing::CandidatePendingAvailability { fn from(value: CandidatePendingAvailability) -> Self { Self { @@ -62,7 +62,7 @@ pub struct BackingState { pub pending_availability: Vec>, } -impl From> for crate::v7::async_backing::BackingState { +impl From> for crate::v8::async_backing::BackingState { fn from(value: BackingState) -> Self { Self { constraints: value.constraints, diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 8bf098c84a82..95d044762710 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -193,25 +193,25 @@ pub enum CandidateEvent { CandidateTimedOut(CandidateReceiptV2, HeadData, CoreIndex), } -impl From> for super::v7::CandidateEvent { +impl From> for super::v8::CandidateEvent { fn from(value: CandidateEvent) -> Self { match value { CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) => - super::v7::CandidateEvent::CandidateBacked( + super::v8::CandidateEvent::CandidateBacked( receipt.into(), head_data, core_index, group_index, ), CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) => - super::v7::CandidateEvent::CandidateIncluded( + super::v8::CandidateEvent::CandidateIncluded( receipt.into(), head_data, core_index, group_index, ), CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) => - super::v7::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), + super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), } } } @@ -275,13 +275,13 @@ impl Ord for CommittedCandidateReceiptV2 { } } -impl From> for super::v7::CommittedCandidateReceipt { +impl From> for super::v8::CommittedCandidateReceipt { fn from(value: CommittedCandidateReceiptV2) -> Self { Self { descriptor: value.descriptor.into(), commitments: value.commitments } } } -impl From> for super::v7::CandidateReceipt { +impl From> for super::v8::CandidateReceipt { fn from(value: CandidateReceiptV2) -> Self { Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } } @@ -609,7 +609,7 @@ pub struct ScrapedOnChainVotes { pub disputes: MultiDisputeStatementSet, } -impl From> for super::v7::ScrapedOnChainVotes { +impl From> for super::v8::ScrapedOnChainVotes { fn from(value: ScrapedOnChainVotes) -> Self { Self { session: value.session, @@ -672,7 +672,7 @@ pub enum CoreState { Free, } -impl From> for super::v7::OccupiedCore { +impl From> for super::v8::OccupiedCore { fn from(value: OccupiedCore) -> Self { Self { next_up_on_available: value.next_up_on_available, @@ -687,13 +687,13 @@ impl From> for super::v7::OccupiedCore { } } -impl From> for super::v7::CoreState { +impl From> for super::v8::CoreState { fn from(value: CoreState) -> Self { match value { - CoreState::Free => super::v7::CoreState::Free, - CoreState::Scheduled(core) => super::v7::CoreState::Scheduled(core), + CoreState::Free => super::v8::CoreState::Free, + CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core), CoreState::Occupied(occupied_core) => - super::v7::CoreState::Occupied(occupied_core.into()), + super::v8::CoreState::Occupied(occupied_core.into()), } } } @@ -702,7 +702,7 @@ impl From> for super::v7::CoreState { mod tests { use super::*; use crate::{ - v7::{ + v8::{ tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, CommittedCandidateReceipt, Hash, HeadData, ValidationCode, }, From 04e31a191201eb928796a14c94be8de2e67cf09e Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Fri, 30 Aug 2024 15:58:20 +0300 Subject: [PATCH 38/40] unused Signed-off-by: Andrei Sandu --- polkadot/primitives/src/vstaging/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 95d044762710..57cba85c10d9 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -19,10 +19,10 @@ use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here use super::{ - async_backing::Constraints, Balance, BlakeTwo256, BlockNumber, CandidateCommitments, + async_backing::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, - UncheckedSignedAvailabilityBitfields, ValidationCodeHash, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, }; use bitvec::prelude::*; use sp_application_crypto::ByteArray; @@ -30,7 +30,6 @@ use sp_application_crypto::ByteArray; use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; From 5fd127949c93ff1d28cb455d78d54ebe7e54cc1a Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Fri, 30 Aug 2024 17:38:46 +0300 Subject: [PATCH 39/40] fix Signed-off-by: Andrei Sandu --- polkadot/runtime/parachains/src/paras_inherent/tests.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 0097c1267383..2675ece471cf 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -58,7 +58,7 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned}; + use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned, vstaging::InternalVersion}; use sp_runtime::Perbill; struct TestConfig { @@ -988,6 +988,7 @@ mod enter { fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1633,7 +1634,7 @@ mod sanitizers { use bitvec::order::Lsb0; use polkadot_primitives::{ AvailabilityBitfield, GroupIndex, Hash, Id as ParaId, SignedAvailabilityBitfield, - ValidatorIndex, + ValidatorIndex }; use rstest::rstest; use sp_core::crypto::UncheckedFrom; From 19d6f325b9168908f98d474a0dc3c92223c25048 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Mon, 2 Sep 2024 15:33:17 +0300 Subject: [PATCH 40/40] fix benchmark build Signed-off-by: Andrei Sandu --- .../parachains/src/inclusion/benchmarking.rs | 27 +++++++++---------- .../parachains/src/paras_inherent/tests.rs | 6 +++-- 2 files changed, 17 insertions(+), 16 deletions(-) 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/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 2675ece471cf..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, vstaging::InternalVersion}; + use polkadot_primitives::{ + vstaging::InternalVersion, AvailabilityBitfield, SchedulerParams, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -1634,7 +1636,7 @@ mod sanitizers { use bitvec::order::Lsb0; use polkadot_primitives::{ AvailabilityBitfield, GroupIndex, Hash, Id as ParaId, SignedAvailabilityBitfield, - ValidatorIndex + ValidatorIndex, }; use rstest::rstest; use sp_core::crypto::UncheckedFrom;