cumulus_primitives_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//! Cumulus parachain inherent
18//!
19//! The [`ParachainInherentData`] is the data that is passed by the collator to the parachain
20//! runtime. The runtime will use this data to execute messages from other parachains/the relay
21//! chain or to read data from the relay chain state. When the parachain is validated by a parachain
22//! validator on the relay chain, this data is checked for correctness. If the data passed by the
23//! collator to the runtime isn't correct, the parachain candidate is considered invalid.
24//!
25//! To create a [`ParachainInherentData`] for a specific relay chain block, there exists the
26//! `ParachainInherentDataExt` trait in `cumulus-client-parachain-inherent` that helps with this.
27
28#![cfg_attr(not(feature = "std"), no_std)]
29
30extern crate alloc;
31
32use alloc::{collections::btree_map::BTreeMap, vec::Vec};
33use cumulus_primitives_core::{
34 relay_chain::{
35 ApprovedPeerId, BlakeTwo256, BlockNumber as RelayChainBlockNumber, Hash as RelayHash,
36 HashT as _, Header as RelayHeader,
37 },
38 InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData,
39};
40use scale_info::TypeInfo;
41use sp_inherents::InherentIdentifier;
42
43/// The identifier for the parachain inherent.
44pub const PARACHAIN_INHERENT_IDENTIFIER_V0: InherentIdentifier = *b"sysi1337";
45pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"sysi1338";
46
47/// Legacy ParachainInherentData that is kept around for backward compatibility.
48/// Can be removed once we can safely assume that parachain nodes provide the
49/// `relay_parent_descendants` and `collator_peer_id` fields.
50pub mod v0 {
51 use alloc::{collections::BTreeMap, vec::Vec};
52 use cumulus_primitives_core::{
53 InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData,
54 };
55 use scale_info::TypeInfo;
56
57 /// The inherent data that is passed by the collator to the parachain runtime.
58 #[derive(
59 codec::Encode,
60 codec::Decode,
61 codec::DecodeWithMemTracking,
62 Debug,
63 Clone,
64 PartialEq,
65 TypeInfo,
66 )]
67 pub struct ParachainInherentData {
68 pub validation_data: PersistedValidationData,
69 /// A storage proof of a predefined set of keys from the relay-chain.
70 ///
71 /// Specifically this witness contains the data for:
72 ///
73 /// - the current slot number at the given relay parent
74 /// - active host configuration as per the relay parent,
75 /// - the relay dispatch queue sizes
76 /// - the list of egress HRMP channels (in the list of recipients form)
77 /// - the metadata for the egress HRMP channels
78 pub relay_chain_state: sp_trie::StorageProof,
79 /// Downward messages in the order they were sent.
80 pub downward_messages: Vec<InboundDownwardMessage>,
81 /// HRMP messages grouped by channels. The messages in the inner vec must be in order they
82 /// were sent. In combination with the rule of no more than one message in a channel per
83 /// block, this means `sent_at` is **strictly** greater than the previous one (if any).
84 pub horizontal_messages: BTreeMap<ParaId, Vec<InboundHrmpMessage>>,
85 }
86}
87
88/// The inherent data that is passed by the collator to the parachain runtime.
89#[derive(
90 codec::Encode, codec::Decode, codec::DecodeWithMemTracking, Debug, Clone, PartialEq, TypeInfo,
91)]
92pub struct ParachainInherentData {
93 pub validation_data: PersistedValidationData,
94 /// A storage proof of a predefined set of keys from the relay-chain.
95 ///
96 /// Specifically this witness contains the data for:
97 ///
98 /// - the current slot number at the given relay parent
99 /// - active host configuration as per the relay parent,
100 /// - the relay dispatch queue sizes
101 /// - the list of egress HRMP channels (in the list of recipients form)
102 /// - the metadata for the egress HRMP channels
103 pub relay_chain_state: sp_trie::StorageProof,
104 /// Downward messages in the order they were sent.
105 pub downward_messages: Vec<InboundDownwardMessage>,
106 /// HRMP messages grouped by channels. The messages in the inner vec must be in order they
107 /// were sent. In combination with the rule of no more than one message in a channel per block,
108 /// this means `sent_at` is **strictly** greater than the previous one (if any).
109 pub horizontal_messages: BTreeMap<ParaId, Vec<InboundHrmpMessage>>,
110 /// Contains the relay parent header and its descendants.
111 /// This information is used to ensure that a parachain node builds blocks
112 /// at a specified offset from the chain tip rather than directly at the tip.
113 pub relay_parent_descendants: Vec<RelayHeader>,
114 /// Contains the collator peer ID, which is later sent by the parachain to the
115 /// relay chain via a UMP signal to promote the reputation of the given peer ID.
116 pub collator_peer_id: Option<ApprovedPeerId>,
117}
118
119// Upgrades the ParachainInherentData v0 to the newest format.
120impl Into<ParachainInherentData> for v0::ParachainInherentData {
121 fn into(self) -> ParachainInherentData {
122 ParachainInherentData {
123 validation_data: self.validation_data,
124 relay_chain_state: self.relay_chain_state,
125 downward_messages: self.downward_messages,
126 horizontal_messages: self.horizontal_messages,
127 relay_parent_descendants: Vec::new(),
128 collator_peer_id: None,
129 }
130 }
131}
132
133#[cfg(feature = "std")]
134impl ParachainInherentData {
135 /// Transforms [`ParachainInherentData`] into [`v0::ParachainInherentData`]. Can be used
136 /// to create inherent data compatible with old runtimes.
137 fn as_v0(&self) -> v0::ParachainInherentData {
138 v0::ParachainInherentData {
139 validation_data: self.validation_data.clone(),
140 relay_chain_state: self.relay_chain_state.clone(),
141 downward_messages: self.downward_messages.clone(),
142 horizontal_messages: self.horizontal_messages.clone(),
143 }
144 }
145}
146
147#[cfg(feature = "std")]
148#[async_trait::async_trait]
149impl sp_inherents::InherentDataProvider for ParachainInherentData {
150 async fn provide_inherent_data(
151 &self,
152 inherent_data: &mut sp_inherents::InherentData,
153 ) -> Result<(), sp_inherents::Error> {
154 inherent_data.put_data(PARACHAIN_INHERENT_IDENTIFIER_V0, &self.as_v0())?;
155 inherent_data.put_data(INHERENT_IDENTIFIER, &self)
156 }
157
158 async fn try_handle_error(
159 &self,
160 _: &sp_inherents::InherentIdentifier,
161 _: &[u8],
162 ) -> Option<Result<(), sp_inherents::Error>> {
163 None
164 }
165}
166
167/// An inbound message whose content was hashed.
168#[derive(
169 codec::Encode, codec::Decode, codec::DecodeWithMemTracking, Debug, Clone, PartialEq, TypeInfo,
170)]
171pub struct HashedMessage {
172 pub sent_at: RelayChainBlockNumber,
173 pub msg_hash: sp_core::H256,
174}
175
176impl From<&InboundDownwardMessage<RelayChainBlockNumber>> for HashedMessage {
177 fn from(msg: &InboundDownwardMessage<RelayChainBlockNumber>) -> Self {
178 Self { sent_at: msg.sent_at, msg_hash: MessageQueueChain::hash_msg_data(&msg.msg) }
179 }
180}
181
182impl From<&InboundHrmpMessage> for HashedMessage {
183 fn from(msg: &InboundHrmpMessage) -> Self {
184 Self { sent_at: msg.sent_at, msg_hash: MessageQueueChain::hash_msg_data(&msg.data) }
185 }
186}
187
188/// This struct provides ability to extend a message queue chain (MQC) and compute a new head.
189///
190/// MQC is an instance of a [hash chain] applied to a message queue. Using a hash chain it's
191/// possible to represent a sequence of messages using only a single hash.
192///
193/// A head for an empty chain is agreed to be a zero hash.
194///
195/// An instance is used to track either DMP from the relay chain or HRMP across a channel.
196/// But a given instance is never used to track both. Therefore, you should call either
197/// `extend_downward` or `extend_hrmp`, but not both methods on a single instance.
198///
199/// [hash chain]: https://en.wikipedia.org/wiki/Hash_chain
200#[derive(Default, Clone, codec::Encode, codec::Decode, scale_info::TypeInfo)]
201pub struct MessageQueueChain(RelayHash);
202
203impl MessageQueueChain {
204 /// Create a new instance initialized to `hash`.
205 pub fn new(hash: RelayHash) -> Self {
206 Self(hash)
207 }
208
209 /// Hash the provided message data.
210 fn hash_msg_data(msg: &Vec<u8>) -> sp_core::H256 {
211 BlakeTwo256::hash_of(msg)
212 }
213
214 /// Extend the hash chain with a `HashedMessage`.
215 pub fn extend_with_hashed_msg(&mut self, hashed_msg: &HashedMessage) -> &mut Self {
216 let prev_head = self.0;
217 self.0 = BlakeTwo256::hash_of(&(prev_head, hashed_msg.sent_at, &hashed_msg.msg_hash));
218 self
219 }
220
221 /// Extend the hash chain with an HRMP message. This method should be used only when
222 /// this chain is tracking HRMP.
223 pub fn extend_hrmp(&mut self, horizontal_message: &InboundHrmpMessage) -> &mut Self {
224 self.extend_with_hashed_msg(&horizontal_message.into())
225 }
226
227 /// Extend the hash chain with a downward message. This method should be used only when
228 /// this chain is tracking DMP.
229 pub fn extend_downward(&mut self, downward_message: &InboundDownwardMessage) -> &mut Self {
230 self.extend_with_hashed_msg(&downward_message.into())
231 }
232
233 /// Return the current head of the message queue chain.
234 /// This is agreed to be the zero hash for an empty chain.
235 pub fn head(&self) -> RelayHash {
236 self.0
237 }
238}