1use crate::error::Error;
20use async_trait::async_trait;
21use bp_header_chain::{
22 justification::{
23 verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification,
24 JustificationVerificationContext,
25 },
26 AuthoritySet, ConsensusLogReader, FinalityProof, FindEquivocations, GrandpaConsensusLogReader,
27 HeaderFinalityInfo, HeaderGrandpaInfo, StoredHeaderGrandpaInfo, SubmitFinalityProofCallExtras,
28};
29use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode};
30use codec::{Decode, Encode};
31use futures::stream::StreamExt;
32use num_traits::{One, Zero};
33use relay_substrate_client::{
34 BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf,
35 Subscription,
36};
37use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
38use sp_core::{storage::StorageKey, Bytes};
39use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId};
40use std::{fmt::Debug, marker::PhantomData};
41
42#[async_trait]
44pub trait Engine<C: Chain>: Send {
45 const ID: ConsensusEngineId;
47 type ConsensusLogReader: ConsensusLogReader;
49 type FinalityProof: FinalityProof<HashOf<C>, BlockNumberOf<C>> + Decode + Encode;
51 type FinalityVerificationContext: Debug + Send;
53 type EquivocationProof: Clone + Debug + Send + Sync;
55 type EquivocationsFinder: FindEquivocations<
57 Self::FinalityProof,
58 Self::FinalityVerificationContext,
59 Self::EquivocationProof,
60 >;
61 type KeyOwnerProof: Send;
63 type InitializationData: Debug + Send + Sync + 'static;
65 type OperatingMode: OperatingMode + 'static;
67
68 fn is_initialized_key() -> StorageKey;
73
74 async fn is_initialized<TargetChain: Chain>(
76 target_client: &impl Client<TargetChain>,
77 ) -> Result<bool, SubstrateError> {
78 Ok(target_client
79 .raw_storage_value(target_client.best_header_hash().await?, Self::is_initialized_key())
80 .await?
81 .is_some())
82 }
83
84 fn pallet_operating_mode_key() -> StorageKey;
87
88 async fn is_halted<TargetChain: Chain>(
90 target_client: &impl Client<TargetChain>,
91 ) -> Result<bool, SubstrateError> {
92 Ok(target_client
93 .storage_value::<Self::OperatingMode>(
94 target_client.best_header_hash().await?,
95 Self::pallet_operating_mode_key(),
96 )
97 .await?
98 .map(|operating_mode| operating_mode.is_halted())
99 .unwrap_or(false))
100 }
101
102 async fn source_finality_proofs(
104 source_client: &impl Client<C>,
105 ) -> Result<Subscription<Bytes>, SubstrateError>;
106
107 async fn verify_and_optimize_proof<TargetChain: Chain>(
112 target_client: &impl Client<TargetChain>,
113 header: &C::Header,
114 proof: &mut Self::FinalityProof,
115 ) -> Result<Self::FinalityVerificationContext, SubstrateError>;
116
117 fn check_max_expected_call_limits(
120 header: &C::Header,
121 proof: &Self::FinalityProof,
122 ) -> SubmitFinalityProofCallExtras;
123
124 async fn prepare_initialization_data(
126 client: impl Client<C>,
127 ) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
128
129 async fn finality_verification_context<TargetChain: Chain>(
131 target_client: &impl Client<TargetChain>,
132 at: HashOf<TargetChain>,
133 ) -> Result<Self::FinalityVerificationContext, SubstrateError>;
134
135 async fn synced_headers_finality_info<TargetChain: Chain>(
138 target_client: &impl Client<TargetChain>,
139 at: TargetChain::Hash,
140 ) -> Result<
141 Vec<HeaderFinalityInfo<Self::FinalityProof, Self::FinalityVerificationContext>>,
142 SubstrateError,
143 >;
144
145 async fn generate_source_key_ownership_proof(
147 source_client: &impl Client<C>,
148 at: C::Hash,
149 equivocation: &Self::EquivocationProof,
150 ) -> Result<Self::KeyOwnerProof, SubstrateError>;
151}
152
153pub struct Grandpa<C>(PhantomData<C>);
155
156impl<C: ChainWithGrandpa> Grandpa<C> {
157 async fn source_header(
159 source_client: &impl Client<C>,
160 header_hash: C::Hash,
161 ) -> Result<C::Header, Error<HashOf<C>, BlockNumberOf<C>>> {
162 source_client
163 .header_by_hash(header_hash)
164 .await
165 .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err))
166 }
167
168 async fn source_authorities_set(
170 source_client: &impl Client<C>,
171 header_hash: C::Hash,
172 ) -> Result<GrandpaAuthoritiesSet, Error<HashOf<C>, BlockNumberOf<C>>> {
173 const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
174
175 source_client
176 .state_call(header_hash, SUB_API_GRANDPA_AUTHORITIES.to_string(), ())
177 .await
178 .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))
179 }
180}
181
182#[async_trait]
183impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
184 const ID: ConsensusEngineId = GRANDPA_ENGINE_ID;
185 type ConsensusLogReader = GrandpaConsensusLogReader<<C::Header as Header>::Number>;
186 type FinalityProof = GrandpaJustification<HeaderOf<C>>;
187 type FinalityVerificationContext = JustificationVerificationContext;
188 type EquivocationProof = sp_consensus_grandpa::EquivocationProof<HashOf<C>, BlockNumberOf<C>>;
189 type EquivocationsFinder = GrandpaEquivocationsFinder<C>;
190 type KeyOwnerProof = C::KeyOwnerProof;
191 type InitializationData = bp_header_chain::InitializationData<C::Header>;
192 type OperatingMode = BasicOperatingMode;
193
194 fn is_initialized_key() -> StorageKey {
195 bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
196 }
197
198 fn pallet_operating_mode_key() -> StorageKey {
199 bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME)
200 }
201
202 async fn source_finality_proofs(
203 client: &impl Client<C>,
204 ) -> Result<Subscription<Bytes>, SubstrateError> {
205 client.subscribe_grandpa_finality_justifications().await
206 }
207
208 async fn verify_and_optimize_proof<TargetChain: Chain>(
209 target_client: &impl Client<TargetChain>,
210 header: &C::Header,
211 proof: &mut Self::FinalityProof,
212 ) -> Result<Self::FinalityVerificationContext, SubstrateError> {
213 let verification_context = Grandpa::<C>::finality_verification_context(
214 target_client,
215 target_client.best_header().await?.hash(),
216 )
217 .await?;
218 verify_and_optimize_justification(
223 (header.hash(), *header.number()),
224 &verification_context,
225 proof,
226 )
227 .map(|_| verification_context)
228 .map_err(|e| {
229 SubstrateError::Custom(format!(
230 "Failed to optimize {} GRANDPA jutification for header {:?}: {:?}",
231 C::NAME,
232 header.id(),
233 e,
234 ))
235 })
236 }
237
238 fn check_max_expected_call_limits(
239 header: &C::Header,
240 proof: &Self::FinalityProof,
241 ) -> SubmitFinalityProofCallExtras {
242 bp_header_chain::submit_finality_proof_limits_extras::<C>(header, proof)
243 }
244
245 async fn prepare_initialization_data(
247 source_client: impl Client<C>,
248 ) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>> {
249 let mut justifications = Self::source_finality_proofs(&source_client)
257 .await
258 .map_err(|err| Error::Subscribe(C::NAME, err))?;
259 let justification = justifications
261 .next()
262 .await
263 .ok_or(Error::ReadJustificationStreamEnded(C::NAME))?;
264
265 let justification: GrandpaJustification<C::Header> =
267 Decode::decode(&mut &justification.0[..])
268 .map_err(|err| Error::DecodeJustification(C::NAME, err))?;
269
270 let (initial_header_hash, initial_header_number) =
271 (justification.commit.target_hash, justification.commit.target_number);
272
273 let initial_header = Self::source_header(&source_client, initial_header_hash).await?;
274 log::trace!(target: "bridge", "Selected {} initial header: {}/{}",
275 C::NAME,
276 initial_header_number,
277 initial_header_hash,
278 );
279
280 let initial_authorities_set =
282 Self::source_authorities_set(&source_client, initial_header_hash).await?;
283 log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}",
284 C::NAME,
285 initial_authorities_set,
286 );
287
288 let mut authorities_for_verification = initial_authorities_set.clone();
291 let scheduled_change = GrandpaConsensusLogReader::<BlockNumberOf<C>>::find_scheduled_change(
292 initial_header.digest(),
293 );
294 assert!(
295 scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true),
296 "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\
297 regular change to have zero delay",
298 initial_header_hash,
299 scheduled_change.as_ref().map(|c| c.delay),
300 );
301 let schedules_change = scheduled_change.is_some();
302 if schedules_change {
303 authorities_for_verification =
304 Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?;
305 log::trace!(
306 target: "bridge",
307 "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}",
308 C::NAME,
309 authorities_for_verification,
310 );
311 }
312
313 let mut initial_authorities_set_id = 0;
315 let mut min_possible_block_number = C::BlockNumber::zero();
316 loop {
317 log::trace!(
318 target: "bridge", "Trying {} GRANDPA authorities set id: {}",
319 C::NAME,
320 initial_authorities_set_id,
321 );
322
323 let is_valid_set_id = verify_and_optimize_justification(
324 (initial_header_hash, initial_header_number),
325 &AuthoritySet {
326 authorities: authorities_for_verification.clone(),
327 set_id: initial_authorities_set_id,
328 }
329 .try_into()
330 .map_err(|_| {
331 Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification.clone())
332 })?,
333 &mut justification.clone(),
334 )
335 .is_ok();
336
337 if is_valid_set_id {
338 break
339 }
340
341 initial_authorities_set_id += 1;
342 min_possible_block_number += One::one();
343 if min_possible_block_number > initial_header_number {
344 return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number))
348 }
349 }
350
351 Ok(bp_header_chain::InitializationData {
352 header: Box::new(initial_header),
353 authority_list: initial_authorities_set,
354 set_id: if schedules_change {
355 initial_authorities_set_id + 1
356 } else {
357 initial_authorities_set_id
358 },
359 operating_mode: BasicOperatingMode::Normal,
360 })
361 }
362
363 async fn finality_verification_context<TargetChain: Chain>(
364 target_client: &impl Client<TargetChain>,
365 at: HashOf<TargetChain>,
366 ) -> Result<Self::FinalityVerificationContext, SubstrateError> {
367 let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key(
368 C::WITH_CHAIN_GRANDPA_PALLET_NAME,
369 );
370 let authority_set: AuthoritySet = target_client
371 .storage_value(at, current_authority_set_key)
372 .await?
373 .map(Ok)
374 .unwrap_or(Err(SubstrateError::Custom(format!(
375 "{} `CurrentAuthoritySet` is missing from the {} storage",
376 C::NAME,
377 TargetChain::NAME,
378 ))))?;
379
380 authority_set.try_into().map_err(|e| {
381 SubstrateError::Custom(format!(
382 "{} `CurrentAuthoritySet` from the {} storage is invalid: {e:?}",
383 C::NAME,
384 TargetChain::NAME,
385 ))
386 })
387 }
388
389 async fn synced_headers_finality_info<TargetChain: Chain>(
390 target_client: &impl Client<TargetChain>,
391 at: TargetChain::Hash,
392 ) -> Result<Vec<HeaderGrandpaInfo<HeaderOf<C>>>, SubstrateError> {
393 let stored_headers_grandpa_info: Vec<StoredHeaderGrandpaInfo<HeaderOf<C>>> = target_client
394 .state_call(at, C::SYNCED_HEADERS_GRANDPA_INFO_METHOD.to_string(), ())
395 .await?;
396
397 let mut headers_grandpa_info = vec![];
398 for stored_header_grandpa_info in stored_headers_grandpa_info {
399 headers_grandpa_info.push(stored_header_grandpa_info.try_into().map_err(|e| {
400 SubstrateError::Custom(format!(
401 "{} `AuthoritySet` synced to {} is invalid: {e:?} ",
402 C::NAME,
403 TargetChain::NAME,
404 ))
405 })?);
406 }
407
408 Ok(headers_grandpa_info)
409 }
410
411 async fn generate_source_key_ownership_proof(
412 source_client: &impl Client<C>,
413 at: C::Hash,
414 equivocation: &Self::EquivocationProof,
415 ) -> Result<Self::KeyOwnerProof, SubstrateError> {
416 let set_id = equivocation.set_id();
417 let offender = equivocation.offender();
418
419 let opaque_key_owner_proof = source_client
420 .generate_grandpa_key_ownership_proof(at, set_id, offender.clone())
421 .await?
422 .ok_or(SubstrateError::Custom(format!(
423 "Couldn't get GRANDPA key ownership proof from {} at block: {at} \
424 for offender: {:?}, set_id: {set_id} ",
425 C::NAME,
426 offender.clone(),
427 )))?;
428
429 let key_owner_proof =
430 opaque_key_owner_proof.decode().ok_or(SubstrateError::Custom(format!(
431 "Couldn't decode GRANDPA `OpaqueKeyOwnnershipProof` from {} at block: {at}
432 to `{:?}` for offender: {:?}, set_id: {set_id}, at block: {at}",
433 C::NAME,
434 <C::KeyOwnerProof as TypeInfo>::type_info().path,
435 offender.clone(),
436 )))?;
437
438 Ok(key_owner_proof)
439 }
440}