// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Support of different finality engines, available in Substrate. use crate::error::Error; use async_trait::async_trait; use bp_header_chain::{ find_grandpa_authorities_scheduled_change, justification::{verify_justification, GrandpaJustification}, FinalityProof, }; use codec::{Decode, Encode}; use finality_grandpa::voter_set::VoterSet; use num_traits::{One, Zero}; use relay_substrate_client::{ BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, Subscription, }; use sp_core::{storage::StorageKey, Bytes}; use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet; use sp_runtime::{traits::Header, ConsensusEngineId}; use std::marker::PhantomData; /// Finality enfine, used by the Substrate chain. #[async_trait] pub trait Engine { /// Unique consensus engine identifier. const ID: ConsensusEngineId; /// Type of finality proofs, used by consensus engine. type FinalityProof: FinalityProof> + Decode + Encode; /// Type of bridge pallet initialization data. type InitializationData: std::fmt::Debug + Send + Sync + 'static; /// Returns storage key at the bridged (target) chain that corresponds to the `bool` value, /// which is true when the bridge pallet is halted. fn is_halted_key() -> StorageKey; /// Returns storage at the bridged (target) chain that corresponds to some value that is /// missing from the storage until bridge pallet is initialized. /// /// Note that we don't care about type of the value - just if it present or not. fn is_initialized_key() -> StorageKey; /// A method to subscribe to encoded finality proofs, given source client. async fn finality_proofs(client: Client) -> Result, SubstrateError>; /// Prepare initialization data for the finality bridge pallet. async fn prepare_initialization_data( client: Client, ) -> Result, BlockNumberOf>>; } /// GRANDPA finality engine. pub struct Grandpa(PhantomData); impl Grandpa { /// Read header by hash from the source client. async fn source_header( source_client: &Client, header_hash: C::Hash, ) -> Result, BlockNumberOf>> { source_client .header_by_hash(header_hash) .await .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err)) } /// Read GRANDPA authorities set at given header. async fn source_authorities_set( source_client: &Client, header_hash: C::Hash, ) -> Result, BlockNumberOf>> { let raw_authorities_set = source_client .grandpa_authorities_set(header_hash) .await .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))?; GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) .map_err(|err| Error::DecodeAuthorities(C::NAME, header_hash, err)) } } #[async_trait] impl Engine for Grandpa { const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID; type FinalityProof = GrandpaJustification>; type InitializationData = bp_header_chain::InitializationData; fn is_halted_key() -> StorageKey { bp_header_chain::storage_keys::is_halted_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) } fn is_initialized_key() -> StorageKey { bp_header_chain::storage_keys::best_finalized_hash_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) } async fn finality_proofs(client: Client) -> Result, SubstrateError> { client.subscribe_grandpa_justifications().await } /// Prepare initialization data for the GRANDPA verifier pallet. async fn prepare_initialization_data( source_client: Client, ) -> Result, BlockNumberOf>> { // In ideal world we just need to get best finalized header and then to read GRANDPA // authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at // this header. // // But now there are problems with this approach - `CurrentSetId` may return invalid value. // So here we're waiting for the next justification, read the authorities set and then try // to figure out the set id with bruteforce. let justifications = source_client .subscribe_grandpa_justifications() .await .map_err(|err| Error::Subscribe(C::NAME, err))?; // Read next justification - the header that it finalizes will be used as initial header. let justification = justifications .next() .await .map_err(|e| Error::ReadJustification(C::NAME, e)) .and_then(|justification| { justification.ok_or(Error::ReadJustificationStreamEnded(C::NAME)) })?; // Read initial header. let justification: GrandpaJustification = Decode::decode(&mut &justification.0[..]) .map_err(|err| Error::DecodeJustification(C::NAME, err))?; let (initial_header_hash, initial_header_number) = (justification.commit.target_hash, justification.commit.target_number); let initial_header = Self::source_header(&source_client, initial_header_hash).await?; log::trace!(target: "bridge", "Selected {} initial header: {}/{}", C::NAME, initial_header_number, initial_header_hash, ); // Read GRANDPA authorities set at initial header. let initial_authorities_set = Self::source_authorities_set(&source_client, initial_header_hash).await?; log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", C::NAME, initial_authorities_set, ); // If initial header changes the GRANDPA authorities set, then we need previous authorities // to verify justification. let mut authorities_for_verification = initial_authorities_set.clone(); let scheduled_change = find_grandpa_authorities_scheduled_change(&initial_header); assert!( scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ regular hange to have zero delay", initial_header_hash, scheduled_change.as_ref().map(|c| c.delay), ); let schedules_change = scheduled_change.is_some(); if schedules_change { authorities_for_verification = Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?; log::trace!( target: "bridge", "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", C::NAME, authorities_for_verification, ); } // Now let's try to guess authorities set id by verifying justification. let mut initial_authorities_set_id = 0; let mut min_possible_block_number = C::BlockNumber::zero(); let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()) .ok_or(Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification))?; loop { log::trace!( target: "bridge", "Trying {} GRANDPA authorities set id: {}", C::NAME, initial_authorities_set_id, ); let is_valid_set_id = verify_justification::( (initial_header_hash, initial_header_number), initial_authorities_set_id, &authorities_for_verification, &justification, ) .is_ok(); if is_valid_set_id { break } initial_authorities_set_id += 1; min_possible_block_number += One::one(); if min_possible_block_number > initial_header_number { // there can't be more authorities set changes than headers => if we have reached // `initial_block_number` and still have not found correct value of // `initial_authorities_set_id`, then something else is broken => fail return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number)) } } Ok(bp_header_chain::InitializationData { header: Box::new(initial_header), authority_list: initial_authorities_set, set_id: if schedules_change { initial_authorities_set_id + 1 } else { initial_authorities_set_id }, is_halted: false, }) } }