1use codec::{Codec, Encode};
36use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface;
37use cumulus_client_consensus_common::{
38 self as consensus_common, ParachainBlockImportMarker, ParentSearchParams,
39};
40use cumulus_primitives_aura::AuraUnincludedSegmentApi;
41use cumulus_primitives_core::{
42 CollectCollationInfo, KeyToIncludeInRelayProof, PersistedValidationData,
43};
44use cumulus_relay_chain_interface::RelayChainInterface;
45use polkadot_node_primitives::{SegmentCollation, SubmitSegmentParams};
46use polkadot_node_subsystem::messages::CollationGenerationMessage;
47use polkadot_overseer::Handle as OverseerHandle;
48use polkadot_primitives::{
49 CandidateDescriptorVersion, CollatorPair, Id as ParaId, OccupiedCoreAssumption,
50};
51use sp_consensus::Environment;
52
53use crate::{
54 collator as collator_util,
55 collators::{claim_queue_at, BackingGroupConnectionHelper},
56 export_pov_to_path,
57};
58use futures::prelude::*;
59use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf};
60use sc_consensus::BlockImport;
61use sc_network_types::PeerId;
62use sp_api::{ApiExt, ProvideRuntimeApi};
63use sp_application_crypto::AppPublic;
64use sp_blockchain::HeaderBackend;
65use sp_consensus_aura::{AuraApi, Slot};
66use sp_core::crypto::Pair;
67use sp_inherents::CreateInherentDataProviders;
68use sp_keystore::KeystorePtr;
69use sp_runtime::{
70 traits::{Block as BlockT, Header as HeaderT, Member},
71 BoundedVec, Saturating,
72};
73use sp_timestamp::Timestamp;
74use std::{path::PathBuf, sync::Arc, time::Duration};
75
76pub struct Params<BI, CIDP, Client, Backend, RClient, CHP, ProposerFactory, CS> {
78 pub create_inherent_data_providers: CIDP,
82 pub block_import: BI,
84 pub para_client: Arc<Client>,
86 pub para_backend: Arc<Backend>,
88 pub relay_client: RClient,
90 pub code_hash_provider: CHP,
92 pub keystore: KeystorePtr,
94 pub collator_key: CollatorPair,
96 pub collator_peer_id: PeerId,
98 pub para_id: ParaId,
100 pub overseer_handle: OverseerHandle,
102 pub relay_chain_slot_duration: Duration,
104 pub proposer: ProposerFactory,
106 pub collator_service: CS,
108 pub authoring_duration: Duration,
110 pub reinitialize: bool,
112 pub max_pov_percentage: Option<u32>,
115}
116
117fn get_parachain_slot<Block, Client, P>(
121 para_client: &Client,
122 block_hash: Block::Hash,
123 relay_parent_header: &polkadot_primitives::Header,
124 relay_chain_slot_duration: Duration,
125) -> Option<(Slot, Slot, Timestamp)>
126where
127 Block: BlockT,
128 Client: ProvideRuntimeApi<Block>,
129 Client::Api: AuraApi<Block, P>,
130 P: Codec,
131{
132 let slot_duration =
133 match sc_consensus_aura::standalone::slot_duration_at(para_client, block_hash) {
134 Ok(sd) => sd,
135 Err(err) => {
136 tracing::error!(target: crate::LOG_TARGET, ?err, "Failed to acquire parachain slot duration");
137 return None;
138 },
139 };
140
141 tracing::debug!(target: crate::LOG_TARGET, ?slot_duration, ?block_hash, "Parachain slot duration acquired");
142
143 let (relay_slot, timestamp) = consensus_common::get_relay_slot_and_timestamp(
144 relay_parent_header,
145 relay_chain_slot_duration,
146 )?;
147
148 let slot_now = Slot::from_timestamp(timestamp, slot_duration);
149
150 tracing::debug!(
151 target: crate::LOG_TARGET,
152 ?relay_slot,
153 para_slot = ?slot_now,
154 ?timestamp,
155 ?slot_duration,
156 ?relay_chain_slot_duration,
157 "Adjusted relay-chain slot to parachain slot"
158 );
159
160 Some((slot_now, relay_slot, timestamp))
161}
162
163pub fn run<Block, P, BI, CIDP, Client, Backend, RClient, CHP, Proposer, CS>(
165 params: Params<BI, CIDP, Client, Backend, RClient, CHP, Proposer, CS>,
166) -> impl Future<Output = ()> + Send + 'static
167where
168 Block: BlockT,
169 Client: ProvideRuntimeApi<Block>
170 + BlockOf
171 + AuxStore
172 + HeaderBackend<Block>
173 + BlockBackend<Block>
174 + Send
175 + Sync
176 + 'static,
177 Client::Api: AuraApi<Block, P::Public>
178 + CollectCollationInfo<Block>
179 + AuraUnincludedSegmentApi<Block>
180 + KeyToIncludeInRelayProof<Block>,
181 Backend: sc_client_api::Backend<Block> + 'static,
182 RClient: RelayChainInterface + Clone + 'static,
183 CIDP: CreateInherentDataProviders<Block, ()> + 'static,
184 CIDP::InherentDataProviders: Send,
185 BI: BlockImport<Block> + ParachainBlockImportMarker + Send + Sync + 'static,
186 Proposer: Environment<Block> + Clone + Send + Sync + 'static,
187 CS: CollatorServiceInterface<Block> + Send + Sync + 'static,
188 CHP: consensus_common::ValidationCodeHashProvider<Block::Hash> + Send + 'static,
189 P: Pair + Send + Sync + 'static,
190 P::Public: AppPublic + Member + Codec,
191 P::Signature: TryFrom<Vec<u8>> + Member + Codec,
192{
193 run_with_export::<_, P, _, _, _, _, _, _, _, _>(ParamsWithExport { params, export_pov: None })
194}
195
196pub struct ParamsWithExport<BI, CIDP, Client, Backend, RClient, CHP, Proposer, CS> {
198 pub params: Params<BI, CIDP, Client, Backend, RClient, CHP, Proposer, CS>,
200
201 pub export_pov: Option<PathBuf>,
203}
204
205pub fn run_with_export<Block, P, BI, CIDP, Client, Backend, RClient, CHP, Proposer, CS>(
210 ParamsWithExport { mut params, export_pov }: ParamsWithExport<
211 BI,
212 CIDP,
213 Client,
214 Backend,
215 RClient,
216 CHP,
217 Proposer,
218 CS,
219 >,
220) -> impl Future<Output = ()> + Send + 'static
221where
222 Block: BlockT,
223 Client: ProvideRuntimeApi<Block>
224 + BlockOf
225 + AuxStore
226 + HeaderBackend<Block>
227 + BlockBackend<Block>
228 + Send
229 + Sync
230 + 'static,
231 Client::Api: AuraApi<Block, P::Public>
232 + CollectCollationInfo<Block>
233 + AuraUnincludedSegmentApi<Block>
234 + KeyToIncludeInRelayProof<Block>,
235 Backend: sc_client_api::Backend<Block> + 'static,
236 RClient: RelayChainInterface + Clone + 'static,
237 CIDP: CreateInherentDataProviders<Block, ()> + 'static,
238 CIDP::InherentDataProviders: Send,
239 BI: BlockImport<Block> + ParachainBlockImportMarker + Send + Sync + 'static,
240 Proposer: Environment<Block> + Clone + Send + Sync + 'static,
241 CS: CollatorServiceInterface<Block> + Send + Sync + 'static,
242 CHP: consensus_common::ValidationCodeHashProvider<Block::Hash> + Send + 'static,
243 P: Pair + Send + Sync + 'static,
244 P::Public: AppPublic + Member + Codec,
245 P::Signature: TryFrom<Vec<u8>> + Member + Codec,
246{
247 async move {
248 cumulus_client_collator::initialize_collator_subsystems(
249 &mut params.overseer_handle,
250 params.collator_key,
251 params.para_id,
252 params.reinitialize,
253 )
254 .await;
255
256 let mut import_notifications = match params.relay_client.import_notification_stream().await
257 {
258 Ok(s) => s,
259 Err(err) => {
260 tracing::error!(
261 target: crate::LOG_TARGET,
262 ?err,
263 "Failed to initialize consensus: no relay chain import notification stream"
264 );
265
266 return;
267 },
268 };
269
270 let mut collator = {
271 let params = collator_util::Params {
272 create_inherent_data_providers: params.create_inherent_data_providers,
273 block_import: params.block_import,
274 relay_client: params.relay_client.clone(),
275 keystore: params.keystore.clone(),
276 collator_peer_id: params.collator_peer_id,
277 para_id: params.para_id,
278 proposer: params.proposer,
279 collator_service: params.collator_service,
280 };
281
282 collator_util::Collator::<Block, P, _, _, _, _, _>::new(params)
283 };
284
285 let mut connection_helper = BackingGroupConnectionHelper::new(
286 params.keystore.clone(),
287 params.overseer_handle.clone(),
288 );
289
290 while let Some(relay_parent_header) = import_notifications.next().await {
291 let relay_parent = relay_parent_header.hash();
292
293 let Some(core_index) = claim_queue_at(relay_parent, &mut params.relay_client)
294 .await
295 .iter_claims_at_depth_for_para(0, params.para_id)
296 .next()
297 else {
298 tracing::trace!(
299 target: crate::LOG_TARGET,
300 ?relay_parent,
301 ?params.para_id,
302 "Para is not scheduled on any core, skipping import notification",
303 );
304
305 continue;
306 };
307
308 let max_pov_size = match params
309 .relay_client
310 .persisted_validation_data(
311 relay_parent,
312 params.para_id,
313 OccupiedCoreAssumption::Included,
314 )
315 .await
316 {
317 Ok(None) => continue,
318 Ok(Some(pvd)) => pvd.max_pov_size,
319 Err(err) => {
320 tracing::error!(target: crate::LOG_TARGET, ?err, "Failed to gather information from relay-client");
321 continue;
322 },
323 };
324
325 let session_index =
326 match params.relay_client.session_index_for_child(relay_parent).await {
327 Ok(session_index) => session_index,
328 Err(err) => {
329 tracing::error!(
330 target: crate::LOG_TARGET,
331 ?err,
332 ?relay_parent,
333 "Failed to fetch session index."
334 );
335 continue;
336 },
337 };
338
339 let parent_search_result = match crate::collators::find_parent(
340 ¶ms.relay_client,
341 &*params.para_backend,
342 params.para_id,
343 ParentSearchParams::V2 { scheduling_parent: relay_parent },
344 |_| true,
345 )
346 .await
347 {
348 Some(result) => result,
349 None => continue,
350 };
351
352 let included_header = &parent_search_result.included_at_scheduling;
353 let para_client = &*params.para_client;
354 let keystore = ¶ms.keystore;
355 let included_block_hash = included_header.hash();
356
357 let parent_hash = parent_search_result.best_parent_header.hash();
360 let parent_header = parent_search_result.best_parent_header;
361 let initial_parent_depth =
363 (*parent_header.number()).saturating_sub(*included_header.number());
364 let overseer_handle = &mut params.overseer_handle;
365
366 if !collator.collator_service().check_block_status(parent_hash, &parent_header) {
368 continue;
369 }
370
371 if let Some((slot_now, _relay_slot, _timestamp)) = get_parachain_slot::<_, _, P::Public>(
373 para_client,
374 parent_hash,
375 &relay_parent_header,
376 params.relay_chain_slot_duration,
377 ) {
378 let mut runtime_api = para_client.runtime_api();
379 runtime_api
380 .set_call_context(sp_core::traits::CallContext::Onchain { import: false });
381 if let Ok(authorities) = runtime_api.authorities(parent_hash) {
382 connection_helper.update::<P>(slot_now, &authorities).await;
383 }
384 }
385
386 let Some((slot_now, relay_slot, timestamp)) = get_parachain_slot::<_, _, P::Public>(
387 para_client,
388 parent_hash,
389 &relay_parent_header,
390 params.relay_chain_slot_duration,
391 ) else {
392 continue;
393 };
394
395 let Some(slot_claim) = super::claim_slot::<_, _, P>(
396 slot_now,
397 timestamp,
398 parent_hash,
399 para_client,
400 &keystore,
401 )
402 .await
403 else {
404 continue;
405 };
406
407 if !super::can_build_upon::<_, _>(
408 parent_hash,
409 included_block_hash,
410 relay_slot,
411 slot_now,
412 para_client,
413 )
414 .await
415 {
416 continue;
417 }
418
419 tracing::debug!(
420 target: crate::LOG_TARGET,
421 ?relay_parent,
422 unincluded_segment_len = ?initial_parent_depth,
423 "Slot claimed. Building"
424 );
425
426 let validation_data = PersistedValidationData {
427 parent_head: parent_header.encode().into(),
428 relay_parent_number: *relay_parent_header.number(),
429 relay_parent_storage_root: *relay_parent_header.state_root(),
430 max_pov_size,
431 };
432
433 let relay_proof_request =
434 super::get_relay_proof_request(&*params.para_client, parent_hash);
435
436 let (parachain_inherent_data, other_inherent_data) = match collator
437 .create_inherent_data(
438 relay_parent,
439 &validation_data,
440 parent_hash,
441 slot_claim.timestamp(),
442 relay_proof_request,
443 params.collator_peer_id,
444 )
445 .await
446 {
447 Err(err) => {
448 tracing::error!(target: crate::LOG_TARGET, ?err);
449 continue;
450 },
451 Ok(x) => x,
452 };
453
454 let Some(validation_code_hash) = params.code_hash_provider.code_hash_at(parent_hash)
455 else {
456 tracing::error!(target: crate::LOG_TARGET, ?parent_hash, "Could not fetch validation code hash");
457 continue;
458 };
459
460 super::check_validation_code_or_log(
461 &validation_code_hash,
462 params.para_id,
463 ¶ms.relay_client,
464 relay_parent,
465 )
466 .await;
467
468 let allowed_pov_size = if let Some(max_pov_percentage) = params.max_pov_percentage {
469 validation_data.max_pov_size * max_pov_percentage / 100
470 } else {
471 validation_data.max_pov_size * 85 / 100
476 } as usize;
477
478 let collation_result = collator
479 .collate(
480 &parent_header,
481 &slot_claim,
482 None,
483 (parachain_inherent_data, other_inherent_data),
484 params.authoring_duration,
485 allowed_pov_size,
486 None,
487 )
488 .await;
489
490 match collation_result {
491 Ok(Some((collation, block_data))) => {
492 let Some(new_block_header) =
493 block_data.blocks().first().map(|b| b.header().clone())
494 else {
495 tracing::error!(target: crate::LOG_TARGET, "Produced PoV doesn't contain any blocks");
496 continue;
497 };
498
499 let new_block_hash = new_block_header.hash();
500
501 collator.collator_service().announce_block(new_block_hash, None);
504
505 if let Some(ref export_pov) = export_pov {
506 export_pov_to_path::<Block>(
507 export_pov,
508 collation.proof_of_validity.clone().into_compressed(),
509 new_block_hash,
510 *new_block_header.number(),
511 parent_header.clone(),
512 *relay_parent_header.state_root(),
513 *relay_parent_header.number(),
514 validation_data.max_pov_size,
515 );
516 }
517
518 overseer_handle
524 .send_msg(
525 CollationGenerationMessage::SubmitSegment(SubmitSegmentParams {
526 scheduling_parent: relay_parent,
527 core_index,
528 candidates_descriptor_version: CandidateDescriptorVersion::V2,
529 collations: BoundedVec::try_from(vec![SegmentCollation {
530 relay_parent,
531 collation,
532 validation_code_hash,
533 session_index,
534 validation_data,
535 }])
536 .expect("One element segment should fit;qed!"),
537 }),
538 "SubmitSegment",
539 )
540 .await;
541 },
542 Ok(None) => {
543 tracing::debug!(target: crate::LOG_TARGET, "No block proposal");
544 continue;
545 },
546 Err(err) => {
547 tracing::error!(target: crate::LOG_TARGET, ?err);
548 continue;
549 },
550 }
551 }
552 }
553}