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.
This commit is contained in:
Bastian Köcher
2021-02-11 13:05:17 +01:00
committed by GitHub
parent 119e0859b9
commit ba0bdc84a5
13 changed files with 445 additions and 305 deletions
+20 -2
View File
@@ -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",
+1
View File
@@ -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",
+1
View File
@@ -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"
+49 -226
View File
@@ -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;
@@ -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<Vec<InboundDownwardMessage>> {
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<BTreeMap<ParaId, Vec<InboundHrmpMessage>>> {
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<sp_state_machine::StorageProof> {
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| <Vec<ParaId>>::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| <Vec<ParaId>>::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,
&parachain_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,36 +257,42 @@ 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::<UpwardMessage>::decode(&mut &v[..])) {
let upward_messages =
match upward_messages.map(|v| Vec::<UpwardMessage>::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
},
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[..]))
{
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
"Failed to decode the count of processed downward messages: {:?}", e
);
return None
return None;
}
None => 0,
};
@@ -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,11 +350,13 @@ where
) -> Option<Collation> {
trace!(target: LOG_TARGET, "Producing candidate");
let last_head =
match Block::Header::decode(&mut &validation_data.parent_head.0[..]) {
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);
error!(
target: LOG_TARGET,
"Could not decode the head data: {:?}", e
);
return None;
}
};
@@ -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)
@@ -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",
]
+14 -10
View File
@@ -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<T: Config> HrmpMessageSender for Module<T> {
impl<T: Config> ProvideInherent for Module<T> {
type Call = Call<T>;
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<Self::Call> {
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<Box<dyn Fn(&BlockTests, RelayChainBlockNumber, &mut PersistedValidationData)>>,
inherent_data_hook:
Option<Box<dyn Fn(&BlockTests, RelayChainBlockNumber, &mut SystemInherentData)>>,
Option<Box<dyn Fn(&BlockTests, RelayChainBlockNumber, &mut ParachainInherentData)>>,
}
impl BlockTests {
@@ -1089,7 +1090,7 @@ mod tests {
fn with_inherent_data<F>(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
};
+3 -9
View File
@@ -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",
]
-31
View File
@@ -39,37 +39,6 @@ pub type InboundHrmpMessage = polkadot_primitives::v1::InboundHrmpMessage<relay_
/// And outbound HRMP message
pub type OutboundHrmpMessage = polkadot_primitives::v1::OutboundHrmpMessage<ParaId>;
/// 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<InboundDownwardMessage>,
/// 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<ParaId, Vec<InboundHrmpMessage>>,
}
}
/// Well known keys for values in the storage.
pub mod well_known_keys {
/// The storage key for the upward messages.
@@ -0,0 +1,39 @@
[package]
name = "cumulus-primitives-parachain-inherent"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
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",
]
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<PClient>(
polkadot_client: &PClient,
para_id: ParaId,
relay_parent: PHash,
) -> Option<Vec<InboundDownwardMessage>>
where
PClient: ProvideRuntimeApi<PBlock>,
PClient::Api: ParachainHost<PBlock>,
{
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<PClient>(
polkadot_client: &PClient,
para_id: ParaId,
relay_parent: PHash,
) -> Option<BTreeMap<ParaId, Vec<InboundHrmpMessage>>>
where
PClient: ProvideRuntimeApi<PBlock>,
PClient::Api: ParachainHost<PBlock>,
{
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<PBlock>,
para_id: ParaId,
relay_parent: PHash,
) -> Option<sp_state_machine::StorageProof> {
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| <Vec<ParaId>>::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| <Vec<ParaId>>::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<PClient>(
relay_parent: PHash,
polkadot_client: &PClient,
polkadot_backend: &impl Backend<PBlock>,
validation_data: &PersistedValidationData,
para_id: ParaId,
) -> Option<ParachainInherentData>
where
PClient: ProvideRuntimeApi<PBlock>,
PClient::Api: ParachainHost<PBlock>,
{
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,
})
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<InboundDownwardMessage>,
/// 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<ParaId, Vec<InboundHrmpMessage>>,
}
+1
View File
@@ -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" }
+5 -6
View File
@@ -15,11 +15,10 @@
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
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(),