Skip to main content

cumulus_client_parachain_inherent/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Client side code for generating the parachain inherent.
18
19mod mock;
20
21use std::collections::{HashMap, HashSet};
22
23use codec::Decode;
24use cumulus_primitives_core::{
25	relay_chain::{
26		self, ApprovedPeerId, Block as RelayBlock, Hash as PHash, Header as RelayHeader,
27		HrmpChannelId,
28	},
29	ParaId, PersistedValidationData, RelayProofRequest, RelayStorageKey,
30};
31pub use cumulus_primitives_parachain_inherent::{ParachainInherentData, INHERENT_IDENTIFIER};
32use cumulus_relay_chain_interface::RelayChainInterface;
33pub use mock::{MockValidationDataInherentDataProvider, MockXcmConfig};
34use sc_network_types::PeerId;
35use sp_state_machine::StorageProof;
36use sp_storage::ChildInfo;
37
38const LOG_TARGET: &str = "parachain-inherent";
39
40/// Builds the list of static relay chain storage keys that are always needed for parachain
41/// validation.
42async fn get_static_relay_storage_keys(
43	relay_chain_interface: &impl RelayChainInterface,
44	para_id: ParaId,
45	relay_parent: PHash,
46	include_authorities: bool,
47	include_next_authorities: bool,
48) -> Option<HashSet<Vec<u8>>> {
49	use relay_chain::well_known_keys as relay_well_known_keys;
50
51	let ingress_channels = relay_chain_interface
52		.get_storage_by_key(
53			relay_parent,
54			&relay_well_known_keys::hrmp_ingress_channel_index(para_id),
55		)
56		.await
57		.map_err(|e| {
58			tracing::error!(
59				target: LOG_TARGET,
60				relay_parent = ?relay_parent,
61				error = ?e,
62				"Cannot obtain the hrmp ingress channel."
63			)
64		})
65		.ok()?;
66
67	let ingress_channels = ingress_channels
68		.map(|raw| <Vec<ParaId>>::decode(&mut &raw[..]))
69		.transpose()
70		.map_err(|e| {
71			tracing::error!(
72				target: LOG_TARGET,
73				error = ?e,
74				"Cannot decode the hrmp ingress channel index.",
75			)
76		})
77		.ok()?
78		.unwrap_or_default();
79
80	let egress_channels = relay_chain_interface
81		.get_storage_by_key(
82			relay_parent,
83			&relay_well_known_keys::hrmp_egress_channel_index(para_id),
84		)
85		.await
86		.map_err(|e| {
87			tracing::error!(
88				target: LOG_TARGET,
89				error = ?e,
90				"Cannot obtain the hrmp egress channel.",
91			)
92		})
93		.ok()?;
94
95	let egress_channels = egress_channels
96		.map(|raw| <Vec<ParaId>>::decode(&mut &raw[..]))
97		.transpose()
98		.map_err(|e| {
99			tracing::error!(
100				target: LOG_TARGET,
101				error = ?e,
102				"Cannot decode the hrmp egress channel index.",
103			)
104		})
105		.ok()?
106		.unwrap_or_default();
107
108	let mut relevant_keys: HashSet<Vec<u8>> = HashSet::from([
109		relay_well_known_keys::CURRENT_BLOCK_RANDOMNESS.to_vec(),
110		relay_well_known_keys::ONE_EPOCH_AGO_RANDOMNESS.to_vec(),
111		relay_well_known_keys::TWO_EPOCHS_AGO_RANDOMNESS.to_vec(),
112		relay_well_known_keys::CURRENT_SLOT.to_vec(),
113		relay_well_known_keys::ACTIVE_CONFIG.to_vec(),
114		relay_well_known_keys::dmq_mqc_head(para_id),
115		// TODO paritytech/polkadot#6283: Remove all usages of `relay_dispatch_queue_size`
116		// We need to keep this here until all parachains have migrated to
117		// `relay_dispatch_queue_remaining_capacity`.
118		#[allow(deprecated)]
119		relay_well_known_keys::relay_dispatch_queue_size(para_id),
120		relay_well_known_keys::relay_dispatch_queue_remaining_capacity(para_id).key,
121		relay_well_known_keys::hrmp_ingress_channel_index(para_id),
122		relay_well_known_keys::hrmp_egress_channel_index(para_id),
123		relay_well_known_keys::upgrade_go_ahead_signal(para_id),
124		relay_well_known_keys::upgrade_restriction_signal(para_id),
125		relay_well_known_keys::para_head(para_id),
126	]);
127	relevant_keys.extend(ingress_channels.into_iter().map(|sender| {
128		relay_well_known_keys::hrmp_channels(HrmpChannelId { sender, recipient: para_id })
129	}));
130	relevant_keys.extend(egress_channels.into_iter().map(|recipient| {
131		relay_well_known_keys::hrmp_channels(HrmpChannelId { sender: para_id, recipient })
132	}));
133
134	if include_authorities {
135		relevant_keys.insert(relay_well_known_keys::AUTHORITIES.to_vec());
136	}
137
138	if include_next_authorities {
139		relevant_keys.insert(relay_well_known_keys::NEXT_AUTHORITIES.to_vec());
140	}
141
142	Some(relevant_keys)
143}
144
145/// Collect the relevant relay chain state in form of a proof for putting it into the validation
146/// data inherent.
147async fn collect_relay_storage_proof(
148	relay_chain_interface: &impl RelayChainInterface,
149	para_id: ParaId,
150	relay_parent: PHash,
151	include_authorities: bool,
152	include_next_authorities: bool,
153	relay_proof_request: RelayProofRequest,
154) -> Option<StorageProof> {
155	// Get static keys that are always needed.
156	let mut all_top_keys = get_static_relay_storage_keys(
157		relay_chain_interface,
158		para_id,
159		relay_parent,
160		include_authorities,
161		include_next_authorities,
162	)
163	.await?;
164
165	// Group requested keys by storage type.
166	let RelayProofRequest { keys } = relay_proof_request;
167	let mut child_keys: HashMap<Vec<u8>, HashSet<Vec<u8>>> = HashMap::new();
168
169	for key in keys {
170		match key {
171			RelayStorageKey::Top(k) => {
172				all_top_keys.insert(k);
173			},
174			RelayStorageKey::Child { storage_key, key } => {
175				child_keys.entry(storage_key).or_default().insert(key);
176			},
177		}
178	}
179
180	// Collect all storage proofs.
181	let mut all_proofs = Vec::new();
182
183	// Collect top-level storage proof.
184	let top_keys_vec: Vec<Vec<u8>> = all_top_keys.into_iter().collect();
185	match relay_chain_interface.prove_read(relay_parent, &top_keys_vec).await {
186		Ok(top_proof) => {
187			all_proofs.push(top_proof);
188		},
189		Err(e) => {
190			tracing::error!(
191				target: LOG_TARGET,
192				relay_parent = ?relay_parent,
193				error = ?e,
194				"Cannot obtain relay chain storage proof.",
195			);
196			return None;
197		},
198	}
199
200	// Collect child trie proofs.
201	for (storage_key, data_keys) in child_keys {
202		let child_info = ChildInfo::new_default(&storage_key);
203		let data_keys_vec: Vec<Vec<u8>> = data_keys.into_iter().collect();
204		match relay_chain_interface
205			.prove_child_read(relay_parent, &child_info, &data_keys_vec)
206			.await
207		{
208			Ok(child_proof) => {
209				all_proofs.push(child_proof);
210			},
211			Err(e) => {
212				tracing::error!(
213					target: LOG_TARGET,
214					relay_parent = ?relay_parent,
215					child_trie_id = ?child_info.storage_key(),
216					error = ?e,
217					"Cannot obtain child trie proof from relay chain.",
218				);
219			},
220		}
221	}
222
223	// Merge all proofs.
224	Some(StorageProof::merge(all_proofs))
225}
226
227pub struct ParachainInherentDataProvider;
228
229impl ParachainInherentDataProvider {
230	/// Create the [`ParachainInherentData`] at the given `relay_parent`.
231	///
232	/// Returns `None` if the creation failed.
233	pub async fn create_at(
234		relay_parent: PHash,
235		relay_chain_interface: &impl RelayChainInterface,
236		validation_data: &PersistedValidationData,
237		para_id: ParaId,
238		relay_parent_descendants: Vec<RelayHeader>,
239		relay_proof_request: RelayProofRequest,
240		collator_peer_id: PeerId,
241	) -> Option<ParachainInherentData> {
242		let collator_peer_id = ApprovedPeerId::try_from(collator_peer_id.to_bytes())
243			.inspect_err(|_e| {
244				tracing::warn!(
245					target: LOG_TARGET,
246					"Could not convert collator_peer_id into ApprovedPeerId. The collator_peer_id \
247					should contain a sequence of at most 64 bytes",
248				);
249			})
250			.ok();
251
252		// Only include next epoch authorities when the descendants include an epoch digest.
253		// Skip the first entry because this is the relay parent itself.
254		let include_next_authorities = relay_parent_descendants
255			.iter()
256			.skip(1)
257			.any(sc_consensus_babe::contains_epoch_change::<RelayBlock>);
258		let relay_chain_state = collect_relay_storage_proof(
259			relay_chain_interface,
260			para_id,
261			relay_parent,
262			!relay_parent_descendants.is_empty(),
263			include_next_authorities,
264			relay_proof_request,
265		)
266		.await?;
267
268		let downward_messages = relay_chain_interface
269			.retrieve_dmq_contents(para_id, relay_parent)
270			.await
271			.map_err(|e| {
272				tracing::error!(
273					target: LOG_TARGET,
274					relay_parent = ?relay_parent,
275					error = ?e,
276					"An error occurred during requesting the downward messages.",
277				);
278			})
279			.ok()?;
280		let horizontal_messages = relay_chain_interface
281			.retrieve_all_inbound_hrmp_channel_contents(para_id, relay_parent)
282			.await
283			.map_err(|e| {
284				tracing::error!(
285					target: LOG_TARGET,
286					relay_parent = ?relay_parent,
287					error = ?e,
288					"An error occurred during requesting the inbound HRMP messages.",
289				);
290			})
291			.ok()?;
292
293		Some(ParachainInherentData {
294			downward_messages,
295			horizontal_messages,
296			validation_data: validation_data.clone(),
297			relay_chain_state,
298			relay_parent_descendants,
299			collator_peer_id,
300		})
301	}
302}