referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/finality_base/
engine.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Support of different finality engines, available in Substrate.
18
19use 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/// Finality engine, used by the Substrate chain.
43#[async_trait]
44pub trait Engine<C: Chain>: Send {
45	/// Unique consensus engine identifier.
46	const ID: ConsensusEngineId;
47	/// A reader that can extract the consensus log from the header digest and interpret it.
48	type ConsensusLogReader: ConsensusLogReader;
49	/// Type of finality proofs, used by consensus engine.
50	type FinalityProof: FinalityProof<HashOf<C>, BlockNumberOf<C>> + Decode + Encode;
51	/// The context needed for verifying finality proofs.
52	type FinalityVerificationContext: Debug + Send;
53	/// The type of the equivocation proof used by the consensus engine.
54	type EquivocationProof: Clone + Debug + Send + Sync;
55	/// The equivocations finder.
56	type EquivocationsFinder: FindEquivocations<
57		Self::FinalityProof,
58		Self::FinalityVerificationContext,
59		Self::EquivocationProof,
60	>;
61	/// The type of the key owner proof used by the consensus engine.
62	type KeyOwnerProof: Send;
63	/// Type of bridge pallet initialization data.
64	type InitializationData: Debug + Send + Sync + 'static;
65	/// Type of bridge pallet operating mode.
66	type OperatingMode: OperatingMode + 'static;
67
68	/// Returns storage at the bridged (target) chain that corresponds to some value that is
69	/// missing from the storage until bridge pallet is initialized.
70	///
71	/// Note that we don't care about type of the value - just if it present or not.
72	fn is_initialized_key() -> StorageKey;
73
74	/// Returns `Ok(true)` if finality pallet at the bridged chain has already been initialized.
75	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	/// Returns storage key at the bridged (target) chain that corresponds to the variable
85	/// that holds the operating mode of the pallet.
86	fn pallet_operating_mode_key() -> StorageKey;
87
88	/// Returns `Ok(true)` if finality pallet at the bridged chain is halted.
89	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	/// A method to subscribe to encoded finality proofs, given source client.
103	async fn source_finality_proofs(
104		source_client: &impl Client<C>,
105	) -> Result<Subscription<Bytes>, SubstrateError>;
106
107	/// Verify and optimize finality proof before sending it to the target node.
108	///
109	/// Apart from optimization, we expect this method to perform all required checks
110	/// that the `header` and `proof` are valid at the current state of the target chain.
111	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	/// Checks whether the given `header` and its finality `proof` fit the maximal expected
118	/// call limits (size and weight).
119	fn check_max_expected_call_limits(
120		header: &C::Header,
121		proof: &Self::FinalityProof,
122	) -> SubmitFinalityProofCallExtras;
123
124	/// Prepare initialization data for the finality bridge pallet.
125	async fn prepare_initialization_data(
126		client: impl Client<C>,
127	) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
128
129	/// Get the context needed for validating a finality proof.
130	async fn finality_verification_context<TargetChain: Chain>(
131		target_client: &impl Client<TargetChain>,
132		at: HashOf<TargetChain>,
133	) -> Result<Self::FinalityVerificationContext, SubstrateError>;
134
135	/// Returns the finality info associated to the source headers synced with the target
136	/// at the provided block.
137	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	/// Generate key ownership proof for the provided equivocation.
146	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
153/// GRANDPA finality engine.
154pub struct Grandpa<C>(PhantomData<C>);
155
156impl<C: ChainWithGrandpa> Grandpa<C> {
157	/// Read header by hash from the source client.
158	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	/// Read GRANDPA authorities set at given header.
169	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		// we're risking with race here - we have decided to submit justification some time ago and
219		// actual authorities set (which we have read now) may have changed, so this
220		// `optimize_justification` may fail. But if target chain is configured properly, it'll fail
221		// anyway, after we submit transaction and failing earlier is better. So - it is fine
222		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	/// Prepare initialization data for the GRANDPA verifier pallet.
246	async fn prepare_initialization_data(
247		source_client: impl Client<C>,
248	) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>> {
249		// In ideal world we just need to get best finalized header and then to read GRANDPA
250		// authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at
251		// this header.
252		//
253		// But now there are problems with this approach - `CurrentSetId` may return invalid value.
254		// So here we're waiting for the next justification, read the authorities set and then try
255		// to figure out the set id with bruteforce.
256		let mut justifications = Self::source_finality_proofs(&source_client)
257			.await
258			.map_err(|err| Error::Subscribe(C::NAME, err))?;
259		// Read next justification - the header that it finalizes will be used as initial header.
260		let justification = justifications
261			.next()
262			.await
263			.ok_or(Error::ReadJustificationStreamEnded(C::NAME))?;
264
265		// Read initial header.
266		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		// Read GRANDPA authorities set at initial header.
281		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		// If initial header changes the GRANDPA authorities set, then we need previous authorities
289		// to verify justification.
290		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		// Now let's try to guess authorities set id by verifying justification.
314		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				// there can't be more authorities set changes than headers => if we have reached
345				// `initial_block_number` and still have not found correct value of
346				// `initial_authorities_set_id`, then something else is broken => fail
347				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}