cumulus_client_consensus_aura/collators/
mod.rs1use crate::collator::SlotClaim;
25use codec::Codec;
26use cumulus_client_consensus_common::{self as consensus_common, ParentSearchParams};
27use cumulus_primitives_aura::{AuraUnincludedSegmentApi, Slot};
28use cumulus_primitives_core::{relay_chain::Header as RelayHeader, BlockT};
29use cumulus_relay_chain_interface::RelayChainInterface;
30use polkadot_node_subsystem::messages::RuntimeApiRequest;
31use polkadot_node_subsystem_util::runtime::ClaimQueueSnapshot;
32use polkadot_primitives::{
33 Hash as RelayHash, Id as ParaId, OccupiedCoreAssumption, ValidationCodeHash,
34 DEFAULT_SCHEDULING_LOOKAHEAD,
35};
36use sc_consensus_aura::{standalone as aura_internal, AuraApi};
37use sp_api::{ApiExt, ProvideRuntimeApi, RuntimeApiInfo};
38use sp_core::Pair;
39use sp_keystore::KeystorePtr;
40use sp_timestamp::Timestamp;
41
42pub mod basic;
43pub mod lookahead;
44pub mod slot_based;
45
46const PARENT_SEARCH_DEPTH: usize = 30;
54
55async fn check_validation_code_or_log(
60 local_validation_code_hash: &ValidationCodeHash,
61 para_id: ParaId,
62 relay_client: &impl RelayChainInterface,
63 relay_parent: RelayHash,
64) {
65 let state_validation_code_hash = match relay_client
66 .validation_code_hash(relay_parent, para_id, OccupiedCoreAssumption::Included)
67 .await
68 {
69 Ok(hash) => hash,
70 Err(error) => {
71 tracing::debug!(
72 target: super::LOG_TARGET,
73 %error,
74 ?relay_parent,
75 %para_id,
76 "Failed to fetch validation code hash",
77 );
78 return
79 },
80 };
81
82 match state_validation_code_hash {
83 Some(state) =>
84 if state != *local_validation_code_hash {
85 tracing::warn!(
86 target: super::LOG_TARGET,
87 %para_id,
88 ?relay_parent,
89 ?local_validation_code_hash,
90 relay_validation_code_hash = ?state,
91 "Parachain code doesn't match validation code stored in the relay chain state.",
92 );
93 },
94 None => {
95 tracing::warn!(
96 target: super::LOG_TARGET,
97 %para_id,
98 ?relay_parent,
99 "Could not find validation code for parachain in the relay chain state.",
100 );
101 },
102 }
103}
104
105async fn scheduling_lookahead(
107 relay_parent: RelayHash,
108 relay_client: &impl RelayChainInterface,
109) -> Option<u32> {
110 let runtime_api_version = relay_client
111 .version(relay_parent)
112 .await
113 .map_err(|e| {
114 tracing::error!(
115 target: super::LOG_TARGET,
116 error = ?e,
117 "Failed to fetch relay chain runtime version.",
118 )
119 })
120 .ok()?;
121
122 let parachain_host_runtime_api_version = runtime_api_version
123 .api_version(
124 &<dyn polkadot_primitives::runtime_api::ParachainHost<polkadot_primitives::Block>>::ID,
125 )
126 .unwrap_or_default();
127
128 if parachain_host_runtime_api_version <
129 RuntimeApiRequest::SCHEDULING_LOOKAHEAD_RUNTIME_REQUIREMENT
130 {
131 return None
132 }
133
134 match relay_client.scheduling_lookahead(relay_parent).await {
135 Ok(scheduling_lookahead) => Some(scheduling_lookahead),
136 Err(err) => {
137 tracing::error!(
138 target: crate::LOG_TARGET,
139 ?err,
140 ?relay_parent,
141 "Failed to fetch scheduling lookahead from relay chain",
142 );
143 None
144 },
145 }
146}
147
148async fn claim_queue_at(
150 relay_parent: RelayHash,
151 relay_client: &impl RelayChainInterface,
152) -> ClaimQueueSnapshot {
153 match relay_client.claim_queue(relay_parent).await {
155 Ok(claim_queue) => claim_queue.into(),
156 Err(error) => {
157 tracing::error!(
158 target: crate::LOG_TARGET,
159 ?error,
160 ?relay_parent,
161 "Failed to query claim queue runtime API",
162 );
163 Default::default()
164 },
165 }
166}
167
168async fn can_build_upon<Block: BlockT, Client, P>(
171 para_slot: Slot,
172 relay_slot: Slot,
173 timestamp: Timestamp,
174 parent_hash: Block::Hash,
175 included_block: Block::Hash,
176 client: &Client,
177 keystore: &KeystorePtr,
178) -> Option<SlotClaim<P::Public>>
179where
180 Client: ProvideRuntimeApi<Block>,
181 Client::Api: AuraApi<Block, P::Public> + AuraUnincludedSegmentApi<Block> + ApiExt<Block>,
182 P: Pair,
183 P::Public: Codec,
184 P::Signature: Codec,
185{
186 let runtime_api = client.runtime_api();
187 let authorities = runtime_api.authorities(parent_hash).ok()?;
188 let author_pub = aura_internal::claim_slot::<P>(para_slot, &authorities, keystore).await?;
189
190 if parent_hash == included_block {
196 return Some(SlotClaim::unchecked::<P>(author_pub, para_slot, timestamp));
197 }
198
199 let api_version = runtime_api
200 .api_version::<dyn AuraUnincludedSegmentApi<Block>>(parent_hash)
201 .ok()
202 .flatten()?;
203
204 let slot = if api_version > 1 { relay_slot } else { para_slot };
205
206 runtime_api
207 .can_build_upon(parent_hash, included_block, slot)
208 .ok()?
209 .then(|| SlotClaim::unchecked::<P>(author_pub, para_slot, timestamp))
210}
211
212async fn find_parent<Block>(
216 relay_parent: RelayHash,
217 para_id: ParaId,
218 para_backend: &impl sc_client_api::Backend<Block>,
219 relay_client: &impl RelayChainInterface,
220) -> Option<(<Block as BlockT>::Header, consensus_common::PotentialParent<Block>)>
221where
222 Block: BlockT,
223{
224 let parent_search_params = ParentSearchParams {
225 relay_parent,
226 para_id,
227 ancestry_lookback: scheduling_lookahead(relay_parent, relay_client)
228 .await
229 .unwrap_or(DEFAULT_SCHEDULING_LOOKAHEAD)
230 .saturating_sub(1) as usize,
231 max_depth: PARENT_SEARCH_DEPTH,
232 ignore_alternative_branches: true,
233 };
234
235 let potential_parents = cumulus_client_consensus_common::find_potential_parents::<Block>(
236 parent_search_params,
237 para_backend,
238 relay_client,
239 )
240 .await;
241
242 let potential_parents = match potential_parents {
243 Err(e) => {
244 tracing::error!(
245 target: crate::LOG_TARGET,
246 ?relay_parent,
247 err = ?e,
248 "Could not fetch potential parents to build upon"
249 );
250
251 return None
252 },
253 Ok(x) => x,
254 };
255
256 let included_block = potential_parents.iter().find(|x| x.depth == 0)?.header.clone();
257 potential_parents
258 .into_iter()
259 .max_by_key(|a| a.depth)
260 .map(|parent| (included_block, parent))
261}
262
263#[cfg(test)]
264mod tests {
265 use crate::collators::can_build_upon;
266 use codec::Encode;
267 use cumulus_primitives_aura::Slot;
268 use cumulus_primitives_core::BlockT;
269 use cumulus_relay_chain_interface::PHash;
270 use cumulus_test_client::{
271 runtime::{Block, Hash},
272 Client, DefaultTestClientBuilderExt, InitBlockBuilder, TestClientBuilder,
273 TestClientBuilderExt,
274 };
275 use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
276 use polkadot_primitives::HeadData;
277 use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy};
278 use sp_consensus::BlockOrigin;
279 use sp_keystore::{Keystore, KeystorePtr};
280 use sp_timestamp::Timestamp;
281 use std::sync::Arc;
282
283 async fn import_block<I: BlockImport<Block>>(
284 importer: &I,
285 block: Block,
286 origin: BlockOrigin,
287 import_as_best: bool,
288 ) {
289 let (header, body) = block.deconstruct();
290
291 let mut block_import_params = BlockImportParams::new(origin, header);
292 block_import_params.fork_choice = Some(ForkChoiceStrategy::Custom(import_as_best));
293 block_import_params.body = Some(body);
294 importer.import_block(block_import_params).await.unwrap();
295 }
296
297 fn sproof_with_parent_by_hash(client: &Client, hash: PHash) -> RelayStateSproofBuilder {
298 let header = client.header(hash).ok().flatten().expect("No header for parent block");
299 let included = HeadData(header.encode());
300 let mut builder = RelayStateSproofBuilder::default();
301 builder.para_id = cumulus_test_client::runtime::PARACHAIN_ID.into();
302 builder.included_para_head = Some(included);
303
304 builder
305 }
306 async fn build_and_import_block(client: &Client, included: Hash) -> Block {
307 let sproof = sproof_with_parent_by_hash(client, included);
308
309 let block_builder = client.init_block_builder(None, sproof).block_builder;
310
311 let block = block_builder.build().unwrap().block;
312
313 let origin = BlockOrigin::NetworkInitialSync;
314 import_block(client, block.clone(), origin, true).await;
315 block
316 }
317
318 fn set_up_components() -> (Arc<Client>, KeystorePtr) {
319 let keystore = Arc::new(sp_keystore::testing::MemoryKeystore::new()) as Arc<_>;
320 for key in sp_keyring::Sr25519Keyring::iter() {
321 Keystore::sr25519_generate_new(
322 &*keystore,
323 sp_application_crypto::key_types::AURA,
324 Some(&key.to_seed()),
325 )
326 .expect("Can insert key into MemoryKeyStore");
327 }
328 (Arc::new(TestClientBuilder::new().build()), keystore)
329 }
330
331 #[tokio::test]
337 async fn test_can_build_upon() {
338 let (client, keystore) = set_up_components();
339
340 let genesis_hash = client.chain_info().genesis_hash;
341 let mut last_hash = genesis_hash;
342
343 while can_build_upon::<_, _, sp_consensus_aura::sr25519::AuthorityPair>(
345 Slot::from(u64::MAX),
346 Slot::from(u64::MAX),
347 Timestamp::default(),
348 last_hash,
349 genesis_hash,
350 &*client,
351 &keystore,
352 )
353 .await
354 .is_some()
355 {
356 let block = build_and_import_block(&client, genesis_hash).await;
357 last_hash = block.header().hash();
358 }
359
360 let result = can_build_upon::<_, _, sp_consensus_aura::sr25519::AuthorityPair>(
363 Slot::from(u64::MAX),
364 Slot::from(u64::MAX),
365 Timestamp::default(),
366 last_hash,
367 last_hash,
368 &*client,
369 &keystore,
370 )
371 .await;
372 assert!(result.is_some());
373 }
374}
375
376pub struct RelayParentData {
378 relay_parent: RelayHeader,
380 descendants: Vec<RelayHeader>,
382}
383
384impl RelayParentData {
385 pub fn new(relay_parent: RelayHeader) -> Self {
387 Self { relay_parent, descendants: Default::default() }
388 }
389
390 pub fn new_with_descendants(relay_parent: RelayHeader, descendants: Vec<RelayHeader>) -> Self {
392 Self { relay_parent, descendants }
393 }
394
395 pub fn relay_parent(&self) -> &RelayHeader {
397 &self.relay_parent
398 }
399
400 #[cfg(test)]
402 pub fn descendants_len(&self) -> usize {
403 self.descendants.len()
404 }
405
406 pub fn into_inherent_descendant_list(self) -> Vec<RelayHeader> {
410 let Self { relay_parent, mut descendants } = self;
411
412 if descendants.is_empty() {
413 return Default::default()
414 }
415
416 let mut result = vec![relay_parent];
417 result.append(&mut descendants);
418 result
419 }
420}