From 83de3b7d610fc3482f3f5538a9fbaefe4ac0416e Mon Sep 17 00:00:00 2001
From: asynchronous rob <rphmeier@gmail.com>
Date: Sat, 13 May 2023 00:48:32 -0500
Subject: [PATCH] Relay-parent digest logs for parachains (#2552)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* add digest item for relay-parent to primitives

* add a relay-parent-storage-root digest as a workaround

* more docs

* deposit log in pallet-parachain-system

* even more docs

* fix duplicate imports after botched mertge

* fix hyperlinks in docs

* clean up match

Co-authored-by: Bastian Köcher <git@kchr.de>

* improve docs

* fix typo

* add number to the digest item

---------

Co-authored-by: Bastian Köcher <git@kchr.de>
---
 cumulus/pallets/parachain-system/src/lib.rs   | 10 +++
 cumulus/pallets/parachain-system/src/tests.rs | 15 ++++
 cumulus/primitives/core/src/lib.rs            | 90 ++++++++++++++++++-
 3 files changed, 114 insertions(+), 1 deletion(-)

diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs
index 36ef8d57195..b841820acfc 100644
--- a/cumulus/pallets/parachain-system/src/lib.rs
+++ b/cumulus/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::<T>::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/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs
index a4b1c275b7a..cfbe834983c 100755
--- a/cumulus/pallets/parachain-system/src/tests.rs
+++ b/cumulus/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/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs
index 52770cdf716..752e1aee474 100644
--- a/cumulus/primitives/core/src/lib.rs
+++ b/cumulus/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<B: BlockT> ParachainBlockData<B> {
 	}
 }
 
+/// 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<relay_chain::Hash> {
+	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
+/// <https://github.com/paritytech/cumulus/issues/303> via
+/// <https://github.com/paritytech/polkadot/issues/7191>.
+///
+/// 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<Compact<relay_chain::BlockNumber>>,
+	) -> 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<relay_chain::BlockNumber>) =
+					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.
-- 
GitLab