1use codec::{Codec, Encode};
27use cumulus_client_consensus_common::{
28 ParachainBlockImportMarker, ParachainCandidate, ParachainConsensus,
29};
30use cumulus_primitives_core::{relay_chain::Hash as PHash, PersistedValidationData};
31
32use cumulus_primitives_core::relay_chain::HeadData;
33use futures::lock::Mutex;
34use polkadot_primitives::{BlockNumber as RBlockNumber, Hash as RHash};
35use sc_client_api::{backend::AuxStore, BlockOf};
36use sc_consensus::BlockImport;
37use sc_consensus_slots::{BackoffAuthoringBlocksStrategy, SimpleSlotWorker, SlotInfo};
38use sc_telemetry::TelemetryHandle;
39use sp_api::ProvideRuntimeApi;
40use sp_application_crypto::AppPublic;
41use sp_blockchain::HeaderBackend;
42use sp_consensus::{EnableProofRecording, Environment, ProofRecording, Proposer, SyncOracle};
43use sp_consensus_aura::{AuraApi, SlotDuration};
44use sp_core::crypto::Pair;
45use sp_inherents::CreateInherentDataProviders;
46use sp_keystore::KeystorePtr;
47use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor};
48use std::{
49 convert::TryFrom,
50 fs,
51 fs::File,
52 marker::PhantomData,
53 path::PathBuf,
54 sync::{
55 atomic::{AtomicU64, Ordering},
56 Arc,
57 },
58};
59
60mod import_queue;
61
62pub use import_queue::{build_verifier, import_queue, BuildVerifierParams, ImportQueueParams};
63use polkadot_node_primitives::PoV;
64pub use sc_consensus_aura::{
65 slot_duration, standalone::slot_duration_at, AuraVerifier, BuildAuraWorkerParams,
66 SlotProportion,
67};
68pub use sc_consensus_slots::InherentDataProviderExt;
69
70pub mod collator;
71pub mod collators;
72pub mod equivocation_import_queue;
73
74const LOG_TARGET: &str = "aura::cumulus";
75
76pub struct AuraConsensus<B, CIDP, W> {
78 create_inherent_data_providers: Arc<CIDP>,
79 aura_worker: Arc<Mutex<W>>,
80 slot_duration: SlotDuration,
81 last_slot_processed: Arc<AtomicU64>,
82 _phantom: PhantomData<B>,
83}
84
85impl<B, CIDP, W> Clone for AuraConsensus<B, CIDP, W> {
86 fn clone(&self) -> Self {
87 Self {
88 create_inherent_data_providers: self.create_inherent_data_providers.clone(),
89 aura_worker: self.aura_worker.clone(),
90 slot_duration: self.slot_duration,
91 last_slot_processed: self.last_slot_processed.clone(),
92 _phantom: PhantomData,
93 }
94 }
95}
96
97#[deprecated = "Use the `aura::collators::basic` collator instead"]
99pub struct BuildAuraConsensusParams<PF, BI, CIDP, Client, BS, SO> {
100 pub proposer_factory: PF,
101 pub create_inherent_data_providers: CIDP,
102 pub block_import: BI,
103 pub para_client: Arc<Client>,
104 pub backoff_authoring_blocks: Option<BS>,
105 pub sync_oracle: SO,
106 pub keystore: KeystorePtr,
107 pub force_authoring: bool,
108 pub slot_duration: SlotDuration,
109 pub telemetry: Option<TelemetryHandle>,
110 pub block_proposal_slot_portion: SlotProportion,
111 pub max_block_proposal_slot_portion: Option<SlotProportion>,
112}
113
114impl<B, CIDP> AuraConsensus<B, CIDP, ()>
115where
116 B: BlockT,
117 CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + 'static,
118 CIDP::InherentDataProviders: InherentDataProviderExt,
119{
120 #[allow(deprecated)]
122 #[deprecated = "Use the `aura::collators::basic` collator instead"]
123 pub fn build<P, Client, BI, SO, PF, BS, Error>(
124 BuildAuraConsensusParams {
125 proposer_factory,
126 create_inherent_data_providers,
127 block_import,
128 para_client,
129 backoff_authoring_blocks,
130 sync_oracle,
131 keystore,
132 force_authoring,
133 slot_duration,
134 telemetry,
135 block_proposal_slot_portion,
136 max_block_proposal_slot_portion,
137 }: BuildAuraConsensusParams<PF, BI, CIDP, Client, BS, SO>,
138 ) -> Box<dyn ParachainConsensus<B>>
139 where
140 Client:
141 ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync + 'static,
142 Client::Api: AuraApi<B, P::Public>,
143 BI: BlockImport<B> + ParachainBlockImportMarker + Send + Sync + 'static,
144 SO: SyncOracle + Send + Sync + Clone + 'static,
145 BS: BackoffAuthoringBlocksStrategy<NumberFor<B>> + Send + Sync + 'static,
146 PF: Environment<B, Error = Error> + Send + Sync + 'static,
147 PF::Proposer: Proposer<
148 B,
149 Error = Error,
150 ProofRecording = EnableProofRecording,
151 Proof = <EnableProofRecording as ProofRecording>::Proof,
152 >,
153 Error: std::error::Error + Send + From<sp_consensus::Error> + 'static,
154 P: Pair + 'static,
155 P::Public: AppPublic + Member + Codec,
156 P::Signature: TryFrom<Vec<u8>> + Member + Codec,
157 {
158 let worker = sc_consensus_aura::build_aura_worker::<P, _, _, _, _, _, _, _, _>(
159 BuildAuraWorkerParams {
160 client: para_client,
161 block_import,
162 justification_sync_link: (),
163 proposer_factory,
164 sync_oracle,
165 force_authoring,
166 backoff_authoring_blocks,
167 keystore,
168 telemetry,
169 block_proposal_slot_portion,
170 max_block_proposal_slot_portion,
171 compatibility_mode: sc_consensus_aura::CompatibilityMode::None,
172 },
173 );
174
175 Box::new(AuraConsensus {
176 create_inherent_data_providers: Arc::new(create_inherent_data_providers),
177 aura_worker: Arc::new(Mutex::new(worker)),
178 last_slot_processed: Default::default(),
179 slot_duration,
180 _phantom: PhantomData,
181 })
182 }
183}
184
185impl<B, CIDP, W> AuraConsensus<B, CIDP, W>
186where
187 B: BlockT,
188 CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + 'static,
189 CIDP::InherentDataProviders: InherentDataProviderExt,
190{
191 async fn inherent_data(
195 &self,
196 parent: B::Hash,
197 validation_data: &PersistedValidationData,
198 relay_parent: PHash,
199 ) -> Option<CIDP::InherentDataProviders> {
200 self.create_inherent_data_providers
201 .create_inherent_data_providers(parent, (relay_parent, validation_data.clone()))
202 .await
203 .map_err(|e| {
204 tracing::error!(
205 target: LOG_TARGET,
206 error = ?e,
207 "Failed to create inherent data providers.",
208 )
209 })
210 .ok()
211 }
212}
213
214#[async_trait::async_trait]
215impl<B, CIDP, W> ParachainConsensus<B> for AuraConsensus<B, CIDP, W>
216where
217 B: BlockT,
218 CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + Send + Sync + 'static,
219 CIDP::InherentDataProviders: InherentDataProviderExt + Send,
220 W: SimpleSlotWorker<B> + Send + Sync,
221 W::Proposer: Proposer<B, Proof = <EnableProofRecording as ProofRecording>::Proof>,
222{
223 async fn produce_candidate(
224 &mut self,
225 parent: &B::Header,
226 relay_parent: PHash,
227 validation_data: &PersistedValidationData,
228 ) -> Option<ParachainCandidate<B>> {
229 let inherent_data_providers =
230 self.inherent_data(parent.hash(), validation_data, relay_parent).await?;
231
232 let info = SlotInfo::new(
233 inherent_data_providers.slot(),
234 Box::new(inherent_data_providers),
235 self.slot_duration.as_duration(),
236 parent.clone(),
237 Some((validation_data.max_pov_size / 2) as usize),
242 );
243
244 if self.last_slot_processed.fetch_max(*info.slot, Ordering::Relaxed) >= *info.slot {
253 return None
254 }
255
256 let res = self.aura_worker.lock().await.on_slot(info).await?;
257
258 Some(ParachainCandidate { block: res.block, proof: res.storage_proof })
259 }
260}
261
262pub(crate) fn export_pov_to_path<Block: BlockT>(
269 path: PathBuf,
270 pov: PoV,
271 block_hash: Block::Hash,
272 block_number: NumberFor<Block>,
273 parent_header: Block::Header,
274 relay_parent_storage_root: RHash,
275 relay_parent_number: RBlockNumber,
276 max_pov_size: u32,
277) {
278 if let Err(error) = fs::create_dir_all(&path) {
279 tracing::error!(target: LOG_TARGET, %error, path = %path.display(), "Failed to create PoV export directory");
280 return
281 }
282
283 let mut file = match File::create(path.join(format!("{block_hash:?}_{block_number}.pov"))) {
284 Ok(f) => f,
285 Err(error) => {
286 tracing::error!(target: LOG_TARGET, %error, "Failed to export PoV.");
287 return
288 },
289 };
290
291 pov.encode_to(&mut file);
292 PersistedValidationData {
293 parent_head: HeadData(parent_header.encode()),
294 relay_parent_number,
295 relay_parent_storage_root,
296 max_pov_size,
297 }
298 .encode_to(&mut file);
299}