diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index 36ef8d57195..b841820acfc 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -385,6 +385,16 @@ pub mod pallet { ) .expect("Invalid relay chain state proof"); + // Deposit a log indicating the relay-parent storage root. + // TODO: remove this in favor of the relay-parent's hash after + // https://github.com/paritytech/cumulus/issues/303 + frame_system::Pallet::::deposit_log( + cumulus_primitives_core::rpsr_digest::relay_parent_storage_root_item( + vfp.relay_parent_storage_root, + vfp.relay_parent_number, + ), + ); + // initialization logic: we know that this runs exactly once every block, // which means we can put the initialization logic here to remove the // sequencing problem. diff --git a/pallets/parachain-system/src/tests.rs b/pallets/parachain-system/src/tests.rs index a4b1c275b7a..cfbe834983c 100755 --- a/pallets/parachain-system/src/tests.rs +++ b/pallets/parachain-system/src/tests.rs @@ -1006,3 +1006,18 @@ fn upgrade_version_checks_should_work() { }); } } + +#[test] +fn deposits_relay_parent_storage_root() { + BlockTests::new().add_with_post_test( + 123, + || {}, + || { + let digest = System::digest(); + assert!(cumulus_primitives_core::rpsr_digest::extract_relay_parent_storage_root( + &digest + ) + .is_some()); + }, + ); +} diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 52770cdf716..752e1aee474 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -21,7 +21,7 @@ use codec::{Decode, Encode}; use polkadot_parachain::primitives::HeadData; use scale_info::TypeInfo; -use sp_runtime::{traits::Block as BlockT, RuntimeDebug}; +use sp_runtime::RuntimeDebug; use sp_std::prelude::*; pub use polkadot_core_primitives::InboundDownwardMessage; @@ -33,6 +33,12 @@ pub use polkadot_primitives::{ AbridgedHostConfiguration, AbridgedHrmpChannel, PersistedValidationData, }; +pub use sp_runtime::{ + generic::{Digest, DigestItem}, + traits::Block as BlockT, + ConsensusEngineId, +}; + pub use xcm::latest::prelude::*; /// A module that re-exports relevant relay chain definitions. @@ -198,6 +204,88 @@ impl ParachainBlockData { } } +/// A consensus engine ID indicating that this is a Cumulus Parachain. +pub const CUMULUS_CONSENSUS_ID: ConsensusEngineId = *b"CMLS"; + +/// Consensus header digests for Cumulus parachains. +#[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq)] +pub enum CumulusDigestItem { + /// A digest item indicating the relay-parent a parachain block was built against. + #[codec(index = 0)] + RelayParent(relay_chain::Hash), +} + +impl CumulusDigestItem { + /// Encode this as a Substrate [`DigestItem`]. + pub fn to_digest_item(&self) -> DigestItem { + DigestItem::Consensus(CUMULUS_CONSENSUS_ID, self.encode()) + } +} + +/// Extract the relay-parent from the provided header digest. Returns `None` if none were found. +/// +/// If there are multiple valid digests, this returns the value of the first one, although +/// well-behaving runtimes should not produce headers with more than one. +pub fn extract_relay_parent(digest: &Digest) -> Option { + digest.convert_first(|d| match d { + DigestItem::Consensus(id, val) if id == &CUMULUS_CONSENSUS_ID => + match CumulusDigestItem::decode(&mut &val[..]) { + Ok(CumulusDigestItem::RelayParent(hash)) => Some(hash), + _ => None, + }, + _ => None, + }) +} + +/// Utilities for handling the relay-parent storage root as a digest item. +/// +/// This is not intended to be part of the public API, as it is a workaround for +/// via +/// . +/// +/// Runtimes using the parachain-system pallet are expected to produce this digest item, +/// but will stop as soon as they are able to provide the relay-parent hash directly. +/// +/// The relay-chain storage root is, in practice, a unique identifier of a block +/// in the absence of equivocations (which are slashable). This assumes that the relay chain +/// uses BABE or SASSAFRAS, because the slot and the author's VRF randomness are both included +/// in the relay-chain storage root in both cases. +/// +/// Therefore, the relay-parent storage root is a suitable identifier of unique relay chain +/// blocks in low-value scenarios such as performance optimizations. +#[doc(hidden)] +pub mod rpsr_digest { + use super::{relay_chain, ConsensusEngineId, Decode, Digest, DigestItem, Encode}; + use codec::Compact; + + /// A consensus engine ID for relay-parent storage root digests. + pub const RPSR_CONSENSUS_ID: ConsensusEngineId = *b"RPSR"; + + /// Construct a digest item for relay-parent storage roots. + pub fn relay_parent_storage_root_item( + storage_root: relay_chain::Hash, + number: impl Into>, + ) -> DigestItem { + DigestItem::Consensus(RPSR_CONSENSUS_ID, (storage_root, number.into()).encode()) + } + + /// Extract the relay-parent storage root and number from the provided header digest. Returns `None` + /// if none were found. + pub fn extract_relay_parent_storage_root( + digest: &Digest, + ) -> Option<(relay_chain::Hash, relay_chain::BlockNumber)> { + digest.convert_first(|d| match d { + DigestItem::Consensus(id, val) if id == &RPSR_CONSENSUS_ID => { + let (h, n): (relay_chain::Hash, Compact) = + Decode::decode(&mut &val[..]).ok()?; + + Some((h, n.0)) + }, + _ => None, + }) + } +} + /// Information about a collation. /// /// This was used in version 1 of the [`CollectCollationInfo`] runtime api.