From ba0bdc84a54714e915b8f2adaf987bc500ebc05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 11 Feb 2021 13:05:17 +0100 Subject: [PATCH] Move parachain inherent data into its own crate (#326) This renames and moves the `SystemInherentData` into its own crate. The struct is now called `ParachainInherentData`. Besides moving the struct, this also moves the code for creating this struct into this crate. --- cumulus/Cargo.lock | 22 +- cumulus/Cargo.toml | 1 + cumulus/client/collator/Cargo.toml | 1 + cumulus/client/collator/src/lib.rs | 317 ++++-------------- cumulus/pallets/parachain-system/Cargo.toml | 2 + cumulus/pallets/parachain-system/src/lib.rs | 24 +- cumulus/primitives/core/Cargo.toml | 12 +- cumulus/primitives/core/src/lib.rs | 31 -- .../primitives/parachain-inherent/Cargo.toml | 39 +++ .../parachain-inherent/src/client_side.rs | 225 +++++++++++++ .../primitives/parachain-inherent/src/lib.rs | 64 ++++ cumulus/test/client/Cargo.toml | 1 + cumulus/test/client/src/block_builder.rs | 11 +- 13 files changed, 445 insertions(+), 305 deletions(-) create mode 100644 cumulus/primitives/parachain-inherent/Cargo.toml create mode 100644 cumulus/primitives/parachain-inherent/src/client_side.rs create mode 100644 cumulus/primitives/parachain-inherent/src/lib.rs diff --git a/cumulus/Cargo.lock b/cumulus/Cargo.lock index db493e3ed8..7df4185a1b 100644 --- a/cumulus/Cargo.lock +++ b/cumulus/Cargo.lock @@ -1075,6 +1075,7 @@ version = "0.1.0" dependencies = [ "cumulus-client-network", "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", "cumulus-test-client", "cumulus-test-runtime", "env_logger", @@ -1188,6 +1189,7 @@ name = "cumulus-pallet-parachain-system" version = "0.1.0" dependencies = [ "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", "cumulus-test-client", "cumulus-test-relay-sproof-builder", "env_logger", @@ -1241,18 +1243,34 @@ dependencies = [ "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", - "sp-core", - "sp-inherents", "sp-runtime", "sp-std", "sp-trie", ] +[[package]] +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "tracing", +] + [[package]] name = "cumulus-test-client" version = "0.1.0" dependencies = [ "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", "cumulus-test-relay-sproof-builder", "cumulus-test-runtime", "cumulus-test-service", diff --git a/cumulus/Cargo.toml b/cumulus/Cargo.toml index 5c0c2b39e2..67ab581a54 100644 --- a/cumulus/Cargo.toml +++ b/cumulus/Cargo.toml @@ -6,6 +6,7 @@ members = [ "pallets/parachain-system", "pallets/xcm-handler", "primitives/core", + "primitives/parachain-inherent", "rococo-parachains/", "rococo-parachains/pallets/parachain-info", "rococo-parachains/primitives", diff --git a/cumulus/client/collator/Cargo.toml b/cumulus/client/collator/Cargo.toml index 164bbaa6b5..87a4ce6b7e 100644 --- a/cumulus/client/collator/Cargo.toml +++ b/cumulus/client/collator/Cargo.toml @@ -27,6 +27,7 @@ polkadot-node-subsystem = { git = "https://github.com/paritytech/polkadot", bran # Cumulus dependencies cumulus-client-network = { path = "../network" } cumulus-primitives-core = { path = "../../primitives/core" } +cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } # Other dependencies log = "0.4.8" diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 3a89320492..ae9af02418 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -18,10 +18,9 @@ use cumulus_client_network::WaitToAnnounce; use cumulus_primitives_core::{ - inherents, ParachainBlockData, - well_known_keys, InboundDownwardMessage, InboundHrmpMessage, OutboundHrmpMessage, - PersistedValidationData, relay_chain, + well_known_keys, OutboundHrmpMessage, ParachainBlockData, PersistedValidationData, }; +use cumulus_primitives_parachain_inherent::ParachainInherentData; use sc_client_api::{BlockBackend, StateBackend}; use sp_consensus::{ @@ -41,7 +40,7 @@ use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProt use polkadot_overseer::OverseerHandler; use polkadot_primitives::v1::{ Block as PBlock, BlockData, BlockNumber as PBlockNumber, CollatorPair, Hash as PHash, HeadData, - Id as ParaId, PoV, UpwardMessage, HrmpChannelId, + Id as ParaId, PoV, UpwardMessage, }; use polkadot_service::RuntimeApiCollection; @@ -51,7 +50,7 @@ use log::{debug, error, info, trace}; use futures::prelude::*; -use std::{collections::BTreeMap, marker::PhantomData, sync::Arc, time::Duration}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; use parking_lot::Mutex; @@ -101,10 +100,10 @@ where PF: Environment + 'static + Send, PF::Proposer: Send, BI: BlockImport< - Block, - Error = ConsensusError, - Transaction = >::Transaction, - > + Send + Block, + Error = ConsensusError, + Transaction = >::Transaction, + > + Send + Sync + 'static, BS: BlockBackend, @@ -150,161 +149,6 @@ where } } - /// Returns the whole contents of the downward message queue for the parachain we are collating - /// for. - /// - /// Returns `None` in case of an error. - fn retrieve_dmq_contents(&self, relay_parent: PHash) -> Option> { - self.polkadot_client - .runtime_api() - .dmq_contents_with_context( - &BlockId::hash(relay_parent), - sp_core::ExecutionContext::Importing, - self.para_id, - ) - .map_err(|e| { - error!( - target: LOG_TARGET, - "An error occured during requesting the downward messages for {}: {:?}", - relay_parent, e, - ); - }) - .ok() - } - - /// Returns channels contents for each inbound HRMP channel addressed to the parachain we are - /// collating for. - /// - /// Empty channels are also included. - fn retrieve_all_inbound_hrmp_channel_contents( - &self, - relay_parent: PHash, - ) -> Option>> { - self.polkadot_client - .runtime_api() - .inbound_hrmp_channels_contents_with_context( - &BlockId::hash(relay_parent), - sp_core::ExecutionContext::Importing, - self.para_id, - ) - .map_err(|e| { - error!( - target: LOG_TARGET, - "An error occured during requesting the inbound HRMP messages for {}: {:?}", - relay_parent, e, - ); - }) - .ok() - } - - /// Collect the relevant relay chain state in form of a proof for putting it into the validation - /// data inherent. - fn collect_relay_storage_proof( - &self, - relay_parent: PHash, - ) -> Option { - use relay_chain::well_known_keys as relay_well_known_keys; - - let relay_parent_state_backend = self - .polkadot_backend - .state_at(BlockId::Hash(relay_parent)) - .map_err(|e| { - error!( - target: LOG_TARGET, - "Cannot obtain the state of the relay chain at `{:?}`: {:?}", - relay_parent, - e, - ) - }) - .ok()?; - - let ingress_channels = relay_parent_state_backend - .storage(&relay_well_known_keys::hrmp_ingress_channel_index( - self.para_id, - )) - .map_err(|e| { - error!( - target: LOG_TARGET, - "Cannot obtain the hrmp ingress channel index: {:?}", - e, - ) - }) - .ok()?; - let ingress_channels = ingress_channels - .map(|raw| >::decode(&mut &raw[..])) - .transpose() - .map_err(|e| { - error!( - target: LOG_TARGET, - "Cannot decode the hrmp ingress channel index: {:?}", - e, - ) - }) - .ok()? - .unwrap_or_default(); - - let egress_channels = relay_parent_state_backend - .storage(&relay_well_known_keys::hrmp_egress_channel_index( - self.para_id, - )) - .map_err(|e| { - error!( - target: LOG_TARGET, - "Cannot obtain the hrmp egress channel index: {:?}", - e, - ) - }) - .ok()?; - let egress_channels = egress_channels - .map(|raw| >::decode(&mut &raw[..])) - .transpose() - .map_err(|e| { - error!( - target: LOG_TARGET, - "Cannot decode the hrmp egress channel index: {:?}", - e, - ) - }) - .ok()? - .unwrap_or_default(); - - let mut relevant_keys = vec![]; - relevant_keys.push(relay_well_known_keys::ACTIVE_CONFIG.to_vec()); - relevant_keys.push(relay_well_known_keys::dmq_mqc_head(self.para_id)); - relevant_keys.push(relay_well_known_keys::relay_dispatch_queue_size( - self.para_id, - )); - relevant_keys.push(relay_well_known_keys::hrmp_ingress_channel_index( - self.para_id, - )); - relevant_keys.push(relay_well_known_keys::hrmp_egress_channel_index( - self.para_id, - )); - relevant_keys.extend(ingress_channels.into_iter().map(|sender| { - relay_well_known_keys::hrmp_channels(HrmpChannelId { - sender, - recipient: self.para_id, - }) - })); - relevant_keys.extend(egress_channels.into_iter().map(|recipient| { - relay_well_known_keys::hrmp_channels(HrmpChannelId { - sender: self.para_id, - recipient, - }) - })); - - sp_state_machine::prove_read(relay_parent_state_backend, relevant_keys) - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to collect required relay chain state storage proof at `{:?}`: {:?}", - relay_parent, - e, - ) - }) - .ok() - } - /// Get the inherent data with validation function parameters injected fn inherent_data( &mut self, @@ -317,36 +161,28 @@ where .map_err(|e| { error!( target: LOG_TARGET, - "Failed to create inherent data: {:?}", - e, + "Failed to create inherent data: {:?}", e, ) }) .ok()?; - let system_inherent_data = { - let relay_chain_state = self.collect_relay_storage_proof(relay_parent)?; - let downward_messages = self.retrieve_dmq_contents(relay_parent)?; - let horizontal_messages = - self.retrieve_all_inbound_hrmp_channel_contents(relay_parent)?; - - inherents::SystemInherentData { - downward_messages, - horizontal_messages, - validation_data: validation_data.clone(), - relay_chain_state, - } - }; + let parachain_inherent_data = ParachainInherentData::create_at( + relay_parent, + &*self.polkadot_client, + &*self.polkadot_backend, + validation_data, + self.para_id, + )?; inherent_data .put_data( - inherents::SYSTEM_INHERENT_IDENTIFIER, - &system_inherent_data, + cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER, + ¶chain_inherent_data, ) .map_err(|e| { error!( target: LOG_TARGET, - "Failed to put the system inherent into inherent data: {:?}", - e, + "Failed to put the system inherent into inherent data: {:?}", e, ) }) .ok()?; @@ -371,8 +207,7 @@ where Ok(BlockStatus::InChainPruned) => { error!( target: LOG_TARGET, - "Skipping candidate production, because block `{:?}` is already pruned!", - hash, + "Skipping candidate production, because block `{:?}` is already pruned!", hash, ); false } @@ -394,8 +229,7 @@ where } else { debug!( target: LOG_TARGET, - "Skipping candidate production, because block `{:?}` is unknown.", - hash, + "Skipping candidate production, because block `{:?}` is unknown.", hash, ); } false @@ -403,9 +237,7 @@ where Err(e) => { error!( target: LOG_TARGET, - "Failed to get block status of `{:?}`: {:?}", - hash, - e, + "Failed to get block status of `{:?}`: {:?}", hash, e, ); false } @@ -425,39 +257,45 @@ where let state = match self.backend.state_at(BlockId::Hash(block_hash)) { Ok(state) => state, Err(e) => { - error!(target: LOG_TARGET, "Failed to get state of the freshly built block: {:?}", e); + error!( + target: LOG_TARGET, + "Failed to get state of the freshly built block: {:?}", e + ); return None; } }; state.inspect_state(|| { let upward_messages = sp_io::storage::get(well_known_keys::UPWARD_MESSAGES); - let upward_messages = match upward_messages.map(|v| Vec::::decode(&mut &v[..])) { - Some(Ok(msgs)) => msgs, - Some(Err(e)) => { - error!(target: LOG_TARGET, "Failed to decode upward messages from the build block: {:?}", e); - return None - }, - None => Vec::new(), - }; + let upward_messages = + match upward_messages.map(|v| Vec::::decode(&mut &v[..])) { + Some(Ok(msgs)) => msgs, + Some(Err(e)) => { + error!( + target: LOG_TARGET, + "Failed to decode upward messages from the build block: {:?}", e + ); + return None; + } + None => Vec::new(), + }; let new_validation_code = sp_io::storage::get(well_known_keys::NEW_VALIDATION_CODE); - let processed_downward_messages = sp_io::storage::get(well_known_keys::PROCESSED_DOWNWARD_MESSAGES); - let processed_downward_messages = match processed_downward_messages - .map(|v| u32::decode(&mut &v[..])) - { - Some(Ok(processed_cnt)) => processed_cnt, - Some(Err(e)) => { - error!( - target: LOG_TARGET, - "Failed to decode the count of processed downward messages: {:?}", - e - ); - return None - } - None => 0, - }; + let processed_downward_messages = + sp_io::storage::get(well_known_keys::PROCESSED_DOWNWARD_MESSAGES); + let processed_downward_messages = + match processed_downward_messages.map(|v| u32::decode(&mut &v[..])) { + Some(Ok(processed_cnt)) => processed_cnt, + Some(Err(e)) => { + error!( + target: LOG_TARGET, + "Failed to decode the count of processed downward messages: {:?}", e + ); + return None; + } + None => 0, + }; let horizontal_messages = sp_io::storage::get(well_known_keys::HRMP_OUTBOUND_MESSAGES); let horizontal_messages = match horizontal_messages @@ -467,10 +305,9 @@ where Some(Err(e)) => { error!( target: LOG_TARGET, - "Failed to decode the horizontal messages: {:?}", - e + "Failed to decode the horizontal messages: {:?}", e ); - return None + return None; } None => Vec::new(), }; @@ -481,10 +318,9 @@ where Some(Err(e)) => { error!( target: LOG_TARGET, - "Failed to decode the HRMP watermark: {:?}", - e + "Failed to decode the HRMP watermark: {:?}", e ); - return None + return None; } None => { // If the runtime didn't set `HRMP_WATERMARK`, then it means no messages were @@ -514,14 +350,16 @@ where ) -> Option { trace!(target: LOG_TARGET, "Producing candidate"); - let last_head = - match Block::Header::decode(&mut &validation_data.parent_head.0[..]) { - Ok(x) => x, - Err(e) => { - error!(target: LOG_TARGET, "Could not decode the head data: {:?}", e); - return None; - } - }; + let last_head = match Block::Header::decode(&mut &validation_data.parent_head.0[..]) { + Ok(x) => x, + Err(e) => { + error!( + target: LOG_TARGET, + "Could not decode the head data: {:?}", e + ); + return None; + } + }; let last_head_hash = last_head.hash(); if !self.check_block_status(last_head_hash, &last_head) { @@ -539,13 +377,7 @@ where let proposer = proposer_future .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Could not create proposer: {:?}", - e, - ) - }) + .map_err(|e| error!(target: LOG_TARGET, "Could not create proposer: {:?}", e,)) .ok()?; let inherent_data = self.inherent_data(&validation_data, relay_parent)?; @@ -563,13 +395,7 @@ where RecordProof::Yes, ) .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Proposing failed: {:?}", - e, - ) - }) + .map_err(|e| error!(target: LOG_TARGET, "Proposing failed: {:?}", e,)) .ok()?; let proof = match proof { @@ -619,8 +445,7 @@ where b.storage_proof().encode().len() as f64 / 1024f64, ); - let collation = - self.build_collation(b, block_hash, validation_data.relay_parent_number)?; + let collation = self.build_collation(b, block_hash, validation_data.relay_parent_number)?; let pov_hash = collation.proof_of_validity.hash(); self.wait_to_announce @@ -629,9 +454,7 @@ where info!( target: LOG_TARGET, - "Produced proof-of-validity candidate {:?} from block {:?}.", - pov_hash, - block_hash, + "Produced proof-of-validity candidate {:?} from block {:?}.", pov_hash, block_hash, ); Some(collation) diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index c24299c05f..36b14d2216 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -8,6 +8,7 @@ description = "Base pallet for cumulus-based parachains" [dependencies] # Cumulus dependencies cumulus-primitives-core = { path = "../../primitives/core", default-features = false } +cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent", default-features = false } # Polkadot dependencies polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, features = [ "wasm-api" ], branch = "master" } @@ -68,4 +69,5 @@ std = [ "frame-system/std", "frame-executive/std", "cumulus-primitives-core/std", + "cumulus-primitives-parachain-inherent/std", ] diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index ec712163af..c294268671 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -28,13 +28,13 @@ //! Users must ensure that they register this pallet as an inherent provider. use cumulus_primitives_core::{ - inherents::{SystemInherentData, SYSTEM_INHERENT_IDENTIFIER}, relay_chain, well_known_keys::{self, NEW_VALIDATION_CODE, VALIDATION_DATA}, AbridgedHostConfiguration, DownwardMessageHandler, HrmpMessageHandler, HrmpMessageSender, InboundDownwardMessage, InboundHrmpMessage, OnValidationData, OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender, }; +use cumulus_primitives_parachain_inherent::ParachainInherentData; use frame_support::{ decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, @@ -166,11 +166,11 @@ decl_module! { /// As a side effect, this function upgrades the current validation function /// if the appropriate time has come. #[weight = (0, DispatchClass::Mandatory)] - fn set_validation_data(origin, data: SystemInherentData) -> DispatchResult { + fn set_validation_data(origin, data: ParachainInherentData) -> DispatchResult { ensure_none(origin)?; assert!(!DidUpdateValidationData::exists(), "ValidationData must be updated only once in a block"); - let SystemInherentData { + let ParachainInherentData { validation_data: vfp, relay_chain_state, downward_messages, @@ -798,11 +798,12 @@ impl HrmpMessageSender for Module { impl ProvideInherent for Module { type Call = Call; type Error = sp_inherents::MakeFatalError<()>; - const INHERENT_IDENTIFIER: InherentIdentifier = SYSTEM_INHERENT_IDENTIFIER; + const INHERENT_IDENTIFIER: InherentIdentifier = + cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER; fn create_inherent(data: &InherentData) -> Option { - let data: SystemInherentData = data - .get_data(&SYSTEM_INHERENT_IDENTIFIER) + let data: ParachainInherentData = data + .get_data(&Self::INHERENT_IDENTIFIER) .ok() .flatten() .expect("validation function params are always injected into inherent data; qed"); @@ -1029,7 +1030,7 @@ mod tests { persisted_validation_data_hook: Option>, inherent_data_hook: - Option>, + Option>, } impl BlockTests { @@ -1089,7 +1090,7 @@ mod tests { fn with_inherent_data(mut self, f: F) -> Self where - F: 'static + Fn(&BlockTests, RelayChainBlockNumber, &mut SystemInherentData), + F: 'static + Fn(&BlockTests, RelayChainBlockNumber, &mut ParachainInherentData), { self.inherent_data_hook = Some(Box::new(f)); self @@ -1142,7 +1143,7 @@ mod tests { // to storage; they must also be included in the inherent data. let inherent_data = { let mut inherent_data = InherentData::default(); - let mut system_inherent_data = SystemInherentData { + let mut system_inherent_data = ParachainInherentData { validation_data: vfp.clone(), relay_chain_state, downward_messages: Default::default(), @@ -1152,7 +1153,10 @@ mod tests { hook(self, *n as RelayChainBlockNumber, &mut system_inherent_data); } inherent_data - .put_data(SYSTEM_INHERENT_IDENTIFIER, &system_inherent_data) + .put_data( + cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER, + &system_inherent_data, + ) .expect("failed to put VFP inherent"); inherent_data }; diff --git a/cumulus/primitives/core/Cargo.toml b/cumulus/primitives/core/Cargo.toml index fecb21bd70..35806626e8 100644 --- a/cumulus/primitives/core/Cargo.toml +++ b/cumulus/primitives/core/Cargo.toml @@ -6,33 +6,27 @@ edition = "2018" [dependencies] # Substrate dependencies -sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Polkadot dependencies polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Other dependencies -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive" ] } impl-trait-for-tuples = "0.2.1" - -# Polkadot dependencies -polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive" ] } [features] default = [ "std" ] std = [ - "sp-std/std", "codec/std", + "sp-std/std", "polkadot-primitives/std", "polkadot-parachain/std", - "sp-inherents/std", "polkadot-core-primitives/std", "sp-runtime/std", - "sp-core/std", "sp-trie/std", ] diff --git a/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs index bce9849089..618814b6a4 100644 --- a/cumulus/primitives/core/src/lib.rs +++ b/cumulus/primitives/core/src/lib.rs @@ -39,37 +39,6 @@ pub type InboundHrmpMessage = polkadot_primitives::v1::InboundHrmpMessage; -/// Identifiers and types related to Cumulus Inherents -pub mod inherents { - use super::{InboundDownwardMessage, InboundHrmpMessage, ParaId}; - use sp_inherents::InherentIdentifier; - use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; - - /// The identifier for the parachain-system inherent. - pub const SYSTEM_INHERENT_IDENTIFIER: InherentIdentifier = *b"sysi1337"; - - /// The payload that system inherent carries. - #[derive(codec::Encode, codec::Decode, sp_core::RuntimeDebug, Clone, PartialEq)] - pub struct SystemInherentData { - pub validation_data: crate::PersistedValidationData, - /// A storage proof of a predefined set of keys from the relay-chain. - /// - /// Specifically this witness contains the data for: - /// - /// - active host configuration as per the relay parent, - /// - the relay dispatch queue sizes - /// - the list of egress HRMP channels (in the list of recipients form) - /// - the metadata for the egress HRMP channels - pub relay_chain_state: sp_trie::StorageProof, - /// Downward messages in the order they were sent. - pub downward_messages: Vec, - /// HRMP messages grouped by channels. The messages in the inner vec must be in order they - /// were sent. In combination with the rule of no more than one message in a channel per block, - /// this means `sent_at` is **strictly** greater than the previous one (if any). - pub horizontal_messages: BTreeMap>, - } -} - /// Well known keys for values in the storage. pub mod well_known_keys { /// The storage key for the upward messages. diff --git a/cumulus/primitives/parachain-inherent/Cargo.toml b/cumulus/primitives/parachain-inherent/Cargo.toml new file mode 100644 index 0000000000..57bd179f79 --- /dev/null +++ b/cumulus/primitives/parachain-inherent/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +# Substrate dependencies +sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } +sp-trie = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } + +# Cumulus dependencies +cumulus-primitives-core = { path = "../core", default-features = false } + +# Other dependencies +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive" ] } +tracing = { version = "0.1.22", optional = true } + +[features] +default = [ "std" ] +std = [ + "codec/std", + "cumulus-primitives-core/std", + "sp-inherents/std", + "sp-core/std", + "sp-trie/std", + "sp-std/std", + "sp-state-machine", + "tracing", + "sp-runtime", + "sc-client-api", + "sp-api", +] diff --git a/cumulus/primitives/parachain-inherent/src/client_side.rs b/cumulus/primitives/parachain-inherent/src/client_side.rs new file mode 100644 index 0000000000..cedf63915e --- /dev/null +++ b/cumulus/primitives/parachain-inherent/src/client_side.rs @@ -0,0 +1,225 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Substrate 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. + +// Substrate 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 Cumulus. If not, see . + +//! Client side code for generating the parachain inherent. + +use crate::ParachainInherentData; +use codec::Decode; +use cumulus_primitives_core::{ + relay_chain::{ + self, + v1::{HrmpChannelId, ParachainHost}, + Block as PBlock, Hash as PHash, + }, + InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData, +}; +use sc_client_api::Backend; +use sp_api::ProvideRuntimeApi; +use sp_runtime::generic::BlockId; +use sp_state_machine::Backend as _; +use std::collections::BTreeMap; + +const LOG_TARGET: &str = "parachain-inherent"; + +/// Returns the whole contents of the downward message queue for the parachain we are collating +/// for. +/// +/// Returns `None` in case of an error. +fn retrieve_dmq_contents( + polkadot_client: &PClient, + para_id: ParaId, + relay_parent: PHash, +) -> Option> +where + PClient: ProvideRuntimeApi, + PClient::Api: ParachainHost, +{ + polkadot_client + .runtime_api() + .dmq_contents_with_context( + &BlockId::hash(relay_parent), + sp_core::ExecutionContext::Importing, + para_id, + ) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + error = ?e, + "An error occured during requesting the downward messages.", + ); + }) + .ok() +} + +/// Returns channels contents for each inbound HRMP channel addressed to the parachain we are +/// collating for. +/// +/// Empty channels are also included. +fn retrieve_all_inbound_hrmp_channel_contents( + polkadot_client: &PClient, + para_id: ParaId, + relay_parent: PHash, +) -> Option>> +where + PClient: ProvideRuntimeApi, + PClient::Api: ParachainHost, +{ + polkadot_client + .runtime_api() + .inbound_hrmp_channels_contents_with_context( + &BlockId::hash(relay_parent), + sp_core::ExecutionContext::Importing, + para_id, + ) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + error = ?e, + "An error occured during requesting the inbound HRMP messages.", + ); + }) + .ok() +} + +/// Collect the relevant relay chain state in form of a proof for putting it into the validation +/// data inherent. +fn collect_relay_storage_proof( + polkadot_backend: &impl Backend, + para_id: ParaId, + relay_parent: PHash, +) -> Option { + use relay_chain::well_known_keys as relay_well_known_keys; + + let relay_parent_state_backend = polkadot_backend + .state_at(BlockId::Hash(relay_parent)) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + error = ?e, + "Cannot obtain the state of the relay chain.", + ) + }) + .ok()?; + + let ingress_channels = relay_parent_state_backend + .storage(&relay_well_known_keys::hrmp_ingress_channel_index(para_id)) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + error = ?e, + "Cannot obtain the hrmp ingress channel index." + ) + }) + .ok()?; + + let ingress_channels = ingress_channels + .map(|raw| >::decode(&mut &raw[..])) + .transpose() + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + error = ?e, + "Cannot decode the hrmp ingress channel index.", + ) + }) + .ok()? + .unwrap_or_default(); + + let egress_channels = relay_parent_state_backend + .storage(&relay_well_known_keys::hrmp_egress_channel_index(para_id)) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + error = ?e, + "Cannot obtain the hrmp egress channel index.", + ) + }) + .ok()?; + let egress_channels = egress_channels + .map(|raw| >::decode(&mut &raw[..])) + .transpose() + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + error = ?e, + "Cannot decode the hrmp egress channel index.", + ) + }) + .ok()? + .unwrap_or_default(); + + let mut relevant_keys = vec![]; + relevant_keys.push(relay_well_known_keys::ACTIVE_CONFIG.to_vec()); + relevant_keys.push(relay_well_known_keys::dmq_mqc_head(para_id)); + relevant_keys.push(relay_well_known_keys::relay_dispatch_queue_size(para_id)); + relevant_keys.push(relay_well_known_keys::hrmp_ingress_channel_index(para_id)); + relevant_keys.push(relay_well_known_keys::hrmp_egress_channel_index(para_id)); + relevant_keys.extend(ingress_channels.into_iter().map(|sender| { + relay_well_known_keys::hrmp_channels(HrmpChannelId { + sender, + recipient: para_id, + }) + })); + relevant_keys.extend(egress_channels.into_iter().map(|recipient| { + relay_well_known_keys::hrmp_channels(HrmpChannelId { + sender: para_id, + recipient, + }) + })); + + sp_state_machine::prove_read(relay_parent_state_backend, relevant_keys) + .map_err(|e| { + tracing::error!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + error = ?e, + "Failed to collect required relay chain state storage proof.", + ) + }) + .ok() +} + +impl ParachainInherentData { + /// Create the [`ParachainInherentData`] at the given `relay_parent`. + /// + /// Returns `None` if the creation failed. + pub fn create_at( + relay_parent: PHash, + polkadot_client: &PClient, + polkadot_backend: &impl Backend, + validation_data: &PersistedValidationData, + para_id: ParaId, + ) -> Option + where + PClient: ProvideRuntimeApi, + PClient::Api: ParachainHost, + { + let relay_chain_state = collect_relay_storage_proof(polkadot_backend, para_id, relay_parent)?; + let downward_messages = retrieve_dmq_contents(polkadot_client, para_id, relay_parent)?; + let horizontal_messages = + retrieve_all_inbound_hrmp_channel_contents(polkadot_client, para_id, relay_parent)?; + + Some(ParachainInherentData { + downward_messages, + horizontal_messages, + validation_data: validation_data.clone(), + relay_chain_state, + }) + } +} diff --git a/cumulus/primitives/parachain-inherent/src/lib.rs b/cumulus/primitives/parachain-inherent/src/lib.rs new file mode 100644 index 0000000000..3ef23dea48 --- /dev/null +++ b/cumulus/primitives/parachain-inherent/src/lib.rs @@ -0,0 +1,64 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Substrate 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. + +// Substrate 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 Cumulus. If not, see . + +//! Cumulus parachain inherent +//! +//! The [`ParachainInherentData`] is the data that is passed by the collator to the parachain runtime. +//! The runtime will use this data to execute messages from other parachains/the relay chain or to +//! read data from the relay chain state. When the parachain is validated by a parachain validator on +//! the relay chain, this data is checked for correctnes. If the data passed by the collator to the +//! runtime isn't correct, the parachain candidate is considered invalid. +//! +//! Use [`ParachainInherentData::create_at`] to create the [`ParachainInherentData`] at a given +//! relay chain block to include it in a parachain block. + +#![cfg_attr(not(feature = "std"), no_std)] + +use cumulus_primitives_core::{ + InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData, +}; + +use sp_inherents::InherentIdentifier; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + +#[cfg(feature = "std")] +mod client_side; +#[cfg(feature = "std")] +pub use client_side::*; + +/// The identifier for the parachain inherent. +pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"sysi1337"; + +/// The inherent data that is passed by the collator to the parachain runtime. +#[derive(codec::Encode, codec::Decode, sp_core::RuntimeDebug, Clone, PartialEq)] +pub struct ParachainInherentData { + pub validation_data: PersistedValidationData, + /// A storage proof of a predefined set of keys from the relay-chain. + /// + /// Specifically this witness contains the data for: + /// + /// - active host configuration as per the relay parent, + /// - the relay dispatch queue sizes + /// - the list of egress HRMP channels (in the list of recipients form) + /// - the metadata for the egress HRMP channels + pub relay_chain_state: sp_trie::StorageProof, + /// Downward messages in the order they were sent. + pub downward_messages: Vec, + /// HRMP messages grouped by channels. The messages in the inner vec must be in order they + /// were sent. In combination with the rule of no more than one message in a channel per block, + /// this means `sent_at` is **strictly** greater than the previous one (if any). + pub horizontal_messages: BTreeMap>, +} diff --git a/cumulus/test/client/Cargo.toml b/cumulus/test/client/Cargo.toml index b7d586f586..0affbd5693 100644 --- a/cumulus/test/client/Cargo.toml +++ b/cumulus/test/client/Cargo.toml @@ -27,6 +27,7 @@ cumulus-test-runtime = { path = "../runtime" } cumulus-test-service = { path = "../service" } cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } cumulus-primitives-core = { path = "../../primitives/core" } +cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } # Polkadot deps polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/cumulus/test/client/src/block_builder.rs b/cumulus/test/client/src/block_builder.rs index fcd5bfe198..2e494ae5f6 100644 --- a/cumulus/test/client/src/block_builder.rs +++ b/cumulus/test/client/src/block_builder.rs @@ -15,11 +15,10 @@ // along with Cumulus. If not, see . use crate::{Backend, Client}; -use cumulus_primitives_core::{ - inherents::{SystemInherentData, SYSTEM_INHERENT_IDENTIFIER}, PersistedValidationData, -}; -use cumulus_test_runtime::{Block, GetLastTimestamp}; +use cumulus_primitives_core::PersistedValidationData; +use cumulus_primitives_parachain_inherent::{ParachainInherentData, INHERENT_IDENTIFIER}; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use cumulus_test_runtime::{Block, GetLastTimestamp}; use polkadot_primitives::v1::BlockNumber as PBlockNumber; use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; use sp_api::ProvideRuntimeApi; @@ -101,8 +100,8 @@ impl InitBlockBuilder for Client { inherent_data .put_data( - SYSTEM_INHERENT_IDENTIFIER, - &SystemInherentData { + INHERENT_IDENTIFIER, + &ParachainInherentData { validation_data, relay_chain_state, downward_messages: Default::default(),