referrerpolicy=no-referrer-when-downgrade

cumulus_pallet_parachain_system/
relay_state_snapshot.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//! Relay chain state proof provides means for accessing part of relay chain storage for reads.
18
19use alloc::vec::Vec;
20use codec::{Decode, Encode};
21use cumulus_primitives_core::{
22	relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId,
23};
24use scale_info::TypeInfo;
25use sp_runtime::traits::HashingFor;
26use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder};
27use sp_trie::{HashDBT, MemoryDB, StorageProof, EMPTY_PREFIX};
28
29/// The capacity of the upward message queue of a parachain on the relay chain.
30// The field order should stay the same as the data can be found in the proof to ensure both are
31// have the same encoded representation.
32#[derive(Clone, Encode, Decode, TypeInfo, Default)]
33pub struct RelayDispatchQueueRemainingCapacity {
34	/// The number of additional messages that can be enqueued.
35	pub remaining_count: u32,
36	/// The total size of additional messages that can be enqueued.
37	pub remaining_size: u32,
38}
39
40/// A snapshot of some messaging related state of relay chain pertaining to the current parachain.
41///
42/// This data is essential for making sure that the parachain is aware of current resource use on
43/// the relay chain and that the candidates produced for this parachain do not exceed any of these
44/// limits.
45#[derive(Clone, Encode, Decode, TypeInfo)]
46pub struct MessagingStateSnapshot {
47	/// The current message queue chain head for downward message queue.
48	///
49	/// If the value is absent on the relay chain this will be set to all zeros.
50	pub dmq_mqc_head: relay_chain::Hash,
51
52	/// The current capacity of the upward message queue of the current parachain on the relay
53	/// chain.
54	pub relay_dispatch_queue_remaining_capacity: RelayDispatchQueueRemainingCapacity,
55
56	/// Information about all the inbound HRMP channels.
57	///
58	/// These are structured as a list of tuples. The para id in the tuple specifies the sender
59	/// of the channel. Obviously, the recipient is the current parachain.
60	///
61	/// The channels are sorted by the sender para id ascension.
62	pub ingress_channels: Vec<(ParaId, AbridgedHrmpChannel)>,
63
64	/// Information about all the outbound HRMP channels.
65	///
66	/// These are structured as a list of tuples. The para id in the tuple specifies the recipient
67	/// of the channel. Obviously, the sender is the current parachain.
68	///
69	/// The channels are sorted by the recipient para id ascension.
70	pub egress_channels: Vec<(ParaId, AbridgedHrmpChannel)>,
71}
72
73#[derive(Debug)]
74pub enum Error {
75	/// The provided proof was created against unexpected storage root.
76	RootMismatch,
77	/// The entry cannot be read.
78	ReadEntry(ReadEntryErr),
79	/// The optional entry cannot be read.
80	ReadOptionalEntry(ReadEntryErr),
81	/// The slot cannot be extracted.
82	Slot(ReadEntryErr),
83	/// The upgrade go-ahead signal cannot be read.
84	UpgradeGoAhead(ReadEntryErr),
85	/// The upgrade restriction signal cannot be read.
86	UpgradeRestriction(ReadEntryErr),
87	/// The host configuration cannot be extracted.
88	Config(ReadEntryErr),
89	/// The DMQ MQC head cannot be extracted.
90	DmqMqcHead(ReadEntryErr),
91	/// Relay dispatch queue cannot be extracted.
92	RelayDispatchQueueRemainingCapacity(ReadEntryErr),
93	/// The hrmp ingress channel index cannot be extracted.
94	HrmpIngressChannelIndex(ReadEntryErr),
95	/// The hrmp egress channel index cannot be extracted.
96	HrmpEgressChannelIndex(ReadEntryErr),
97	/// The channel identified by the sender and receiver cannot be extracted.
98	HrmpChannel(ParaId, ParaId, ReadEntryErr),
99	/// The latest included parachain head cannot be extracted.
100	ParaHead(ReadEntryErr),
101	/// The relay chain authorities cannot be extracted
102	Authorities(ReadEntryErr),
103	/// The relay chain authorities for the next epoch cannot be extracted
104	NextAuthorities(ReadEntryErr),
105}
106
107#[derive(Debug)]
108pub enum ReadEntryErr {
109	/// The value cannot be extracted from the proof.
110	Proof,
111	/// The value cannot be decoded.
112	Decode,
113	/// The value is expected to be present on the relay chain, but it doesn't exist.
114	Absent,
115}
116
117/// Read an entry given by the key and try to decode it. If the value specified by the key according
118/// to the proof is empty, the `fallback` value will be returned.
119///
120/// Returns `Err` in case the backend can't return the value under the specific key (likely due to
121/// a malformed proof), in case the decoding fails, or in case where the value is empty in the relay
122/// chain state and no fallback was provided.
123fn read_entry<T, B>(backend: &B, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>
124where
125	T: Decode,
126	B: Backend<HashingFor<relay_chain::Block>>,
127{
128	backend
129		.storage(key)
130		.map_err(|_| ReadEntryErr::Proof)?
131		.map(|raw_entry| T::decode(&mut &raw_entry[..]).map_err(|_| ReadEntryErr::Decode))
132		.transpose()?
133		.or(fallback)
134		.ok_or(ReadEntryErr::Absent)
135}
136
137/// Read an optional entry given by the key and try to decode it.
138/// Returns `None` if the value specified by the key according to the proof is empty.
139///
140/// Returns `Err` in case the backend can't return the value under the specific key (likely due to
141/// a malformed proof) or if the value couldn't be decoded.
142fn read_optional_entry<T, B>(backend: &B, key: &[u8]) -> Result<Option<T>, ReadEntryErr>
143where
144	T: Decode,
145	B: Backend<HashingFor<relay_chain::Block>>,
146{
147	match read_entry(backend, key, None) {
148		Ok(v) => Ok(Some(v)),
149		Err(ReadEntryErr::Absent) => Ok(None),
150		Err(err) => Err(err),
151	}
152}
153
154/// A state proof extracted from the relay chain.
155///
156/// This state proof is extracted from the relay chain block we are building on top of.
157pub struct RelayChainStateProof {
158	para_id: ParaId,
159	trie_backend:
160		TrieBackend<MemoryDB<HashingFor<relay_chain::Block>>, HashingFor<relay_chain::Block>>,
161}
162
163impl RelayChainStateProof {
164	/// Create a new instance of `Self`.
165	///
166	/// Returns an error if the given `relay_parent_storage_root` is not the root of the given
167	/// `proof`.
168	pub fn new(
169		para_id: ParaId,
170		relay_parent_storage_root: relay_chain::Hash,
171		proof: StorageProof,
172	) -> Result<Self, Error> {
173		let db = proof.into_memory_db::<HashingFor<relay_chain::Block>>();
174		if !db.contains(&relay_parent_storage_root, EMPTY_PREFIX) {
175			return Err(Error::RootMismatch)
176		}
177		let trie_backend = TrieBackendBuilder::new(db, relay_parent_storage_root).build();
178
179		Ok(Self { para_id, trie_backend })
180	}
181
182	/// Read the [`MessagingStateSnapshot`] from the relay chain state proof.
183	///
184	/// Returns an error if anything failed at reading or decoding.
185	pub fn read_messaging_state_snapshot(
186		&self,
187		host_config: &AbridgedHostConfiguration,
188	) -> Result<MessagingStateSnapshot, Error> {
189		let dmq_mqc_head: relay_chain::Hash = read_entry(
190			&self.trie_backend,
191			&relay_chain::well_known_keys::dmq_mqc_head(self.para_id),
192			Some(Default::default()),
193		)
194		.map_err(Error::DmqMqcHead)?;
195
196		let relay_dispatch_queue_remaining_capacity = read_optional_entry::<
197			RelayDispatchQueueRemainingCapacity,
198			_,
199		>(
200			&self.trie_backend,
201			&relay_chain::well_known_keys::relay_dispatch_queue_remaining_capacity(self.para_id)
202				.key,
203		);
204
205		// TODO paritytech/polkadot#6283: Remove all usages of `relay_dispatch_queue_size`
206		//
207		// When the relay chain and all parachains support
208		// `relay_dispatch_queue_remaining_capacity`, this code here needs to be removed and above
209		// needs to be changed to `read_entry` that returns an error if
210		// `relay_dispatch_queue_remaining_capacity` can not be found/decoded.
211		//
212		// For now we just fallback to the old dispatch queue size on `ReadEntryErr::Absent`.
213		// `ReadEntryErr::Decode` and `ReadEntryErr::Proof` are potentially subject to meddling
214		// by malicious collators, so we reject the block in those cases.
215		let relay_dispatch_queue_remaining_capacity = match relay_dispatch_queue_remaining_capacity
216		{
217			Ok(Some(r)) => r,
218			Ok(None) => {
219				let res = read_entry::<(u32, u32), _>(
220					&self.trie_backend,
221					#[allow(deprecated)]
222					&relay_chain::well_known_keys::relay_dispatch_queue_size(self.para_id),
223					Some((0, 0)),
224				)
225				.map_err(Error::RelayDispatchQueueRemainingCapacity)?;
226
227				let remaining_count = host_config.max_upward_queue_count.saturating_sub(res.0);
228				let remaining_size = host_config.max_upward_queue_size.saturating_sub(res.1);
229				RelayDispatchQueueRemainingCapacity { remaining_count, remaining_size }
230			},
231			Err(e) => return Err(Error::RelayDispatchQueueRemainingCapacity(e)),
232		};
233
234		let ingress_channel_index: Vec<ParaId> = read_entry(
235			&self.trie_backend,
236			&relay_chain::well_known_keys::hrmp_ingress_channel_index(self.para_id),
237			Some(Vec::new()),
238		)
239		.map_err(Error::HrmpIngressChannelIndex)?;
240
241		let egress_channel_index: Vec<ParaId> = read_entry(
242			&self.trie_backend,
243			&relay_chain::well_known_keys::hrmp_egress_channel_index(self.para_id),
244			Some(Vec::new()),
245		)
246		.map_err(Error::HrmpEgressChannelIndex)?;
247
248		let mut ingress_channels = Vec::with_capacity(ingress_channel_index.len());
249		for sender in ingress_channel_index {
250			let channel_id = relay_chain::HrmpChannelId { sender, recipient: self.para_id };
251			let hrmp_channel: AbridgedHrmpChannel = read_entry(
252				&self.trie_backend,
253				&relay_chain::well_known_keys::hrmp_channels(channel_id),
254				None,
255			)
256			.map_err(|read_err| Error::HrmpChannel(sender, self.para_id, read_err))?;
257			ingress_channels.push((sender, hrmp_channel));
258		}
259
260		let mut egress_channels = Vec::with_capacity(egress_channel_index.len());
261		for recipient in egress_channel_index {
262			let channel_id = relay_chain::HrmpChannelId { sender: self.para_id, recipient };
263			let hrmp_channel: AbridgedHrmpChannel = read_entry(
264				&self.trie_backend,
265				&relay_chain::well_known_keys::hrmp_channels(channel_id),
266				None,
267			)
268			.map_err(|read_err| Error::HrmpChannel(self.para_id, recipient, read_err))?;
269			egress_channels.push((recipient, hrmp_channel));
270		}
271
272		// NOTE that ingress_channels and egress_channels promise to be sorted. We satisfy this
273		// property by relying on the fact that `ingress_channel_index` and `egress_channel_index`
274		// are themselves sorted.
275		Ok(MessagingStateSnapshot {
276			dmq_mqc_head,
277			relay_dispatch_queue_remaining_capacity,
278			ingress_channels,
279			egress_channels,
280		})
281	}
282
283	/// Read the [`AbridgedHostConfiguration`] from the relay chain state proof.
284	///
285	/// Returns an error if anything failed at reading or decoding.
286	pub fn read_abridged_host_configuration(&self) -> Result<AbridgedHostConfiguration, Error> {
287		read_entry(&self.trie_backend, relay_chain::well_known_keys::ACTIVE_CONFIG, None)
288			.map_err(Error::Config)
289	}
290
291	/// Read latest included parachain [head data](`relay_chain::HeadData`) from the relay chain
292	/// state proof.
293	///
294	/// Returns an error if anything failed at reading or decoding.
295	pub fn read_included_para_head(&self) -> Result<relay_chain::HeadData, Error> {
296		read_entry(&self.trie_backend, &relay_chain::well_known_keys::para_head(self.para_id), None)
297			.map_err(Error::ParaHead)
298	}
299
300	/// Read relay chain authorities.
301	pub fn read_authorities(
302		&self,
303	) -> Result<Vec<(sp_consensus_babe::AuthorityId, sp_consensus_babe::BabeAuthorityWeight)>, Error>
304	{
305		read_entry(&self.trie_backend, &relay_chain::well_known_keys::AUTHORITIES, None)
306			.map_err(Error::Authorities)
307	}
308
309	/// Read relay chain authorities for the next epoch.
310	pub fn read_next_authorities(
311		&self,
312	) -> Result<
313		Option<Vec<(sp_consensus_babe::AuthorityId, sp_consensus_babe::BabeAuthorityWeight)>>,
314		Error,
315	> {
316		read_optional_entry(&self.trie_backend, &relay_chain::well_known_keys::NEXT_AUTHORITIES)
317			.map_err(Error::NextAuthorities)
318	}
319
320	/// Read the [`Slot`](relay_chain::Slot) from the relay chain state proof.
321	///
322	/// The slot is slot of the relay chain block this state proof was extracted from.
323	///
324	/// Returns an error if anything failed at reading or decoding.
325	pub fn read_slot(&self) -> Result<relay_chain::Slot, Error> {
326		read_entry(&self.trie_backend, relay_chain::well_known_keys::CURRENT_SLOT, None)
327			.map_err(Error::Slot)
328	}
329
330	/// Read the go-ahead signal for the upgrade from the relay chain state proof.
331	///
332	/// The go-ahead specifies whether the parachain can apply the upgrade or should abort it. If
333	/// the value is absent then there is either no judgment by the relay chain yet or no upgrade
334	/// is pending.
335	///
336	/// Returns an error if anything failed at reading or decoding.
337	pub fn read_upgrade_go_ahead_signal(
338		&self,
339	) -> Result<Option<relay_chain::UpgradeGoAhead>, Error> {
340		read_optional_entry(
341			&self.trie_backend,
342			&relay_chain::well_known_keys::upgrade_go_ahead_signal(self.para_id),
343		)
344		.map_err(Error::UpgradeGoAhead)
345	}
346
347	/// Read the upgrade restriction signal for the upgrade from the relay chain state proof.
348	///
349	/// If the upgrade restriction is not `None`, then the parachain cannot signal an upgrade at
350	/// this block.
351	///
352	/// Returns an error if anything failed at reading or decoding.
353	pub fn read_upgrade_restriction_signal(
354		&self,
355	) -> Result<Option<relay_chain::UpgradeRestriction>, Error> {
356		read_optional_entry(
357			&self.trie_backend,
358			&relay_chain::well_known_keys::upgrade_restriction_signal(self.para_id),
359		)
360		.map_err(Error::UpgradeRestriction)
361	}
362
363	/// Read an entry given by the key and try to decode it. If the value specified by the key
364	/// according to the proof is empty, the `fallback` value will be returned.
365	///
366	/// Returns `Err` in case the backend can't return the value under the specific key (likely due
367	/// to a malformed proof), in case the decoding fails, or in case where the value is empty in
368	/// the relay chain state and no fallback was provided.
369	pub fn read_entry<T>(&self, key: &[u8], fallback: Option<T>) -> Result<T, Error>
370	where
371		T: Decode,
372	{
373		read_entry(&self.trie_backend, key, fallback).map_err(Error::ReadEntry)
374	}
375
376	/// Read an optional entry given by the key and try to decode it.
377	///
378	/// Returns `Err` in case the backend can't return the value under the specific key (likely due
379	/// to a malformed proof) or if the value couldn't be decoded.
380	pub fn read_optional_entry<T>(&self, key: &[u8]) -> Result<Option<T>, Error>
381	where
382		T: Decode,
383	{
384		read_optional_entry(&self.trie_backend, key).map_err(Error::ReadOptionalEntry)
385	}
386}