FinalityEngine in substrate relay (#1374)

* introduce FinalityEngine in relay code

* add FinalityEngine to relay

* spelling

* fix test compilation

* Update relays/lib-substrate-relay/src/finality/source.rs

Co-authored-by: Adrian Catangiu <adrian@parity.io>

Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
Svyatoslav Nikolsky
2022-04-14 12:05:57 +03:00
committed by Bastian Köcher
parent 6a4144e8f2
commit b3c8852bcf
23 changed files with 469 additions and 359 deletions
@@ -0,0 +1,226 @@
// 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 <http://www.gnu.org/licenses/>.
//! 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<C: Chain> {
/// Unique consensus engine identifier.
const ID: ConsensusEngineId;
/// Type of finality proofs, used by consensus engine.
type FinalityProof: FinalityProof<BlockNumberOf<C>> + 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<C>) -> Result<Subscription<Bytes>, SubstrateError>;
/// Prepare initialization data for the finality bridge pallet.
async fn prepare_initialization_data(
client: Client<C>,
) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
}
/// GRANDPA finality engine.
pub struct Grandpa<C>(PhantomData<C>);
impl<C: ChainWithGrandpa> Grandpa<C> {
/// Read header by hash from the source client.
async fn source_header(
source_client: &Client<C>,
header_hash: C::Hash,
) -> Result<C::Header, Error<HashOf<C>, BlockNumberOf<C>>> {
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<C>,
header_hash: C::Hash,
) -> Result<GrandpaAuthoritiesSet, Error<HashOf<C>, BlockNumberOf<C>>> {
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<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
const ID: ConsensusEngineId = sp_finality_grandpa::GRANDPA_ENGINE_ID;
type FinalityProof = GrandpaJustification<HeaderOf<C>>;
type InitializationData = bp_header_chain::InitializationData<C::Header>;
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<C>) -> Result<Subscription<Bytes>, SubstrateError> {
client.subscribe_grandpa_justifications().await
}
/// Prepare initialization data for the GRANDPA verifier pallet.
async fn prepare_initialization_data(
source_client: Client<C>,
) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>> {
// 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<C::Header> =
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::<C::Header>(
(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,
})
}
}
@@ -0,0 +1,121 @@
// 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 <http://www.gnu.org/licenses/>.
//! Initialize Substrate -> Substrate finality bridge.
//!
//! Initialization is a transaction that calls `initialize()` function of the
//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header
//! and authorities set from source to target chain. The finality sync starts
//! with this header.
use crate::{error::Error, finality::engine::Engine};
use relay_substrate_client::{BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf};
use sp_core::Bytes;
use sp_runtime::traits::Header as HeaderT;
/// Submit headers-bridge initialization transaction.
pub async fn initialize<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain, F>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: F,
) where
F: FnOnce(TargetChain::Index, E::InitializationData) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
{
let result = do_initialize::<E, _, _, _>(
source_client,
target_client,
target_transactions_signer,
prepare_initialize_transaction,
)
.await;
match result {
Ok(Some(tx_hash)) => log::info!(
target: "bridge",
"Successfully submitted {}-headers bridge initialization transaction to {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
tx_hash,
),
Ok(None) => (),
Err(err) => log::error!(
target: "bridge",
"Failed to submit {}-headers bridge initialization transaction to {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
err,
),
}
}
/// Craft and submit initialization transaction, returning any error that may occur.
async fn do_initialize<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain, F>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: F,
) -> Result<
Option<TargetChain::Hash>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
>
where
F: FnOnce(TargetChain::Index, E::InitializationData) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
{
let is_initialized = is_initialized::<E, SourceChain, TargetChain>(&target_client).await?;
if is_initialized {
log::info!(
target: "bridge",
"{}-headers bridge at {} is already initialized. Skipping",
SourceChain::NAME,
TargetChain::NAME,
);
return Ok(None)
}
let initialization_data = E::prepare_initialization_data(source_client).await?;
log::info!(
target: "bridge",
"Prepared initialization data for {}-headers bridge at {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
initialization_data,
);
let initialization_tx_hash = target_client
.submit_signed_extrinsic(target_transactions_signer, move |_, transaction_nonce| {
prepare_initialize_transaction(transaction_nonce, initialization_data)
})
.await
.map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?;
Ok(Some(initialization_tx_hash))
}
/// Returns `Ok(true)` if bridge has already been initialized.
async fn is_initialized<E: Engine<SourceChain>, SourceChain: Chain, TargetChain: Chain>(
target_client: &Client<TargetChain>,
) -> Result<bool, Error<HashOf<SourceChain>, BlockNumberOf<SourceChain>>> {
Ok(target_client
.raw_storage_value(E::is_initialized_key(), None)
.await
.map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))?
.is_some())
}
@@ -18,7 +18,11 @@
//! finality proofs synchronization pipelines.
use crate::{
finality_source::SubstrateFinalitySource, finality_target::SubstrateFinalityTarget,
finality::{
engine::Engine,
source::{SubstrateFinalityProof, SubstrateFinalitySource},
target::SubstrateFinalityTarget,
},
TransactionParams,
};
@@ -27,13 +31,19 @@ use bp_header_chain::justification::GrandpaJustification;
use finality_relay::FinalitySyncPipeline;
use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
use relay_substrate_client::{
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain,
ChainWithGrandpa, Client, HashOf, HeaderOf, SyncHeader, TransactionSignScheme,
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client,
HashOf, HeaderOf, SyncHeader, TransactionSignScheme,
};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use std::{fmt::Debug, marker::PhantomData};
pub mod engine;
pub mod guards;
pub mod initialize;
pub mod source;
pub mod target;
/// Default limit of recent finality proofs.
///
/// Finality delay of 4096 blocks is unlikely to happen in practice in
@@ -44,10 +54,12 @@ pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
#[async_trait]
pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync {
/// Headers of this chain are submitted to the `TargetChain`.
type SourceChain: ChainWithGrandpa;
type SourceChain: Chain;
/// Headers of the `SourceChain` are submitted to this chain.
type TargetChain: Chain;
/// Finality engine.
type FinalityEngine: Engine<Self::SourceChain>;
/// How submit finality proof call is built?
type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>;
/// Scheme used to sign target chain transactions.
@@ -76,7 +88,7 @@ impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipe
type Hash = HashOf<P::SourceChain>;
type Number = BlockNumberOf<P::SourceChain>;
type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>;
type FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>;
type FinalityProof = SubstrateFinalityProof<P>;
}
/// Different ways of building `submit_finality_proof` calls.
@@ -85,23 +97,26 @@ pub trait SubmitFinalityProofCallBuilder<P: SubstrateFinalitySyncPipeline> {
/// function of bridge GRANDPA module at the target chain.
fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
proof: SubstrateFinalityProof<P>,
) -> CallOf<P::TargetChain>;
}
/// Building `submit_finality_proof` call when you have direct access to the target
/// chain runtime.
pub struct DirectSubmitFinalityProofCallBuilder<P, R, I> {
pub struct DirectSubmitGrandpaFinalityProofCallBuilder<P, R, I> {
_phantom: PhantomData<(P, R, I)>,
}
impl<P, R, I> SubmitFinalityProofCallBuilder<P> for DirectSubmitFinalityProofCallBuilder<P, R, I>
impl<P, R, I> SubmitFinalityProofCallBuilder<P>
for DirectSubmitGrandpaFinalityProofCallBuilder<P, R, I>
where
P: SubstrateFinalitySyncPipeline,
R: BridgeGrandpaConfig<I>,
I: 'static,
R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>,
CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>,
P::FinalityEngine:
Engine<P::SourceChain, FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>>,
{
fn build_submit_finality_proof_call(
header: SyncHeader<HeaderOf<P::SourceChain>>,
@@ -125,22 +140,22 @@ macro_rules! generate_mocked_submit_finality_proof_call_builder {
($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => {
pub struct $mocked_builder;
impl $crate::finality_pipeline::SubmitFinalityProofCallBuilder<$pipeline>
impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline>
for $mocked_builder
{
fn build_submit_finality_proof_call(
header: relay_substrate_client::SyncHeader<
relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain
<$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain
>
>,
proof: bp_header_chain::justification::GrandpaJustification<
relay_substrate_client::HeaderOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain
<$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain
>
>,
) -> relay_substrate_client::CallOf<
<$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::TargetChain
<$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::TargetChain
> {
$bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof))
}
@@ -16,11 +16,10 @@
//! Default generic implementation of finality source for basic Substrate client.
use crate::finality_pipeline::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline};
use crate::finality::{engine::Engine, FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline};
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use bp_header_chain::justification::GrandpaJustification;
use codec::Decode;
use finality_relay::SourceClient;
use futures::stream::{unfold, Stream, StreamExt};
@@ -38,13 +37,19 @@ pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockN
pub type SubstrateFinalityProofsStream<P> = Pin<
Box<
dyn Stream<
Item = GrandpaJustification<
HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>,
>,
Item = <<P as SubstrateFinalitySyncPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalitySyncPipeline>::SourceChain,
>>::FinalityProof,
> + Send,
>,
>;
/// Substrate finality proof. Specific to the used `FinalityEngine`.
pub type SubstrateFinalityProof<P> =
<<P as SubstrateFinalitySyncPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalitySyncPipeline>::SourceChain,
>>::FinalityProof;
/// Substrate node as finality source.
pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> {
client: Client<P::SourceChain>,
@@ -120,7 +125,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
) -> Result<
(
relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>,
Option<GrandpaJustification<HeaderOf<P::SourceChain>>>,
Option<SubstrateFinalityProof<P>>,
),
Error,
> {
@@ -130,9 +135,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
let justification = signed_block
.justification()
.map(|raw_justification| {
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
&mut raw_justification.as_slice(),
)
SubstrateFinalityProof::<P>::decode(&mut raw_justification.as_slice())
})
.transpose()
.map_err(Error::ResponseParseFailed)?;
@@ -142,7 +145,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
Ok(unfold(
self.client.clone().subscribe_justifications().await?,
P::FinalityEngine::finality_proofs(self.client.clone()).await?,
move |subscription| async move {
loop {
let log_error = |err| {
@@ -161,7 +164,7 @@ impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<
.ok()??;
let decoded_justification =
GrandpaJustification::<HeaderOf<P::SourceChain>>::decode(
<P::FinalityEngine as Engine<P::SourceChain>>::FinalityProof::decode(
&mut &next_justification[..],
);
@@ -14,24 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Substrate client as Substrate finality proof target. The chain we connect to should have
//! bridge GRANDPA pallet deployed and provide `<BridgedChainName>FinalityApi` to allow bridging
//! with <BridgedName> chain.
//! Substrate client as Substrate finality proof target.
use crate::{
finality_pipeline::{
FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
finality::{
engine::Engine, source::SubstrateFinalityProof, FinalitySyncPipelineAdapter,
SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
},
TransactionParams,
};
use async_trait::async_trait;
use bp_header_chain::{justification::GrandpaJustification, storage_keys::is_halted_key};
use codec::Encode;
use finality_relay::TargetClient;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithGrandpa, Client, Error, HeaderIdOf, HeaderOf,
SignParam, SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction,
AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SignParam,
SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction,
};
use relay_utils::relay_loop::Client as RelayClient;
use sp_core::{Bytes, Pair};
@@ -51,12 +49,9 @@ impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> {
SubstrateFinalityTarget { client, transaction_params }
}
/// Ensure that the GRANDPA pallet at target chain is active.
/// Ensure that the bridge pallet at target chain is active.
pub async fn ensure_pallet_active(&self) -> Result<(), Error> {
let is_halted = self
.client
.storage_value(is_halted_key(P::SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME), None)
.await?;
let is_halted = self.client.storage_value(P::FinalityEngine::is_halted_key(), None).await?;
if is_halted.unwrap_or(false) {
Err(Error::BridgePalletIsHalted)
} else {
@@ -94,7 +89,7 @@ where
// we can't continue to relay finality if target node is out of sync, because
// it may have already received (some of) headers that we're going to relay
self.client.ensure_synced().await?;
// we can't relay finality if GRANDPA pallet at target chain is halted
// we can't relay finality if bridge pallet at target chain is halted
self.ensure_pallet_active().await?;
Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceChain>(
@@ -109,7 +104,7 @@ where
async fn submit_finality_proof(
&self,
header: SyncHeader<HeaderOf<P::SourceChain>>,
proof: GrandpaJustification<HeaderOf<P::SourceChain>>,
proof: SubstrateFinalityProof<P>,
) -> Result<(), Error> {
let genesis_hash = *self.client.genesis_hash();
let transaction_params = self.transaction_params.clone();
@@ -1,282 +0,0 @@
// 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 <http://www.gnu.org/licenses/>.
//! Initialize Substrate -> Substrate headers bridge.
//!
//! Initialization is a transaction that calls `initialize()` function of the
//! `pallet-bridge-grandpa` pallet. This transaction brings initial header
//! and authorities set from source to target chain. The headers sync starts
//! with this header.
use crate::error::Error;
use bp_header_chain::{
find_grandpa_authorities_scheduled_change,
justification::{verify_justification, GrandpaJustification},
InitializationData,
};
use codec::Decode;
use finality_grandpa::voter_set::VoterSet;
use num_traits::{One, Zero};
use relay_substrate_client::{
BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf,
};
use sp_core::Bytes;
use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet;
use sp_runtime::traits::Header as HeaderT;
/// Submit headers-bridge initialization transaction.
pub async fn initialize<SourceChain: ChainWithGrandpa, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: impl FnOnce(
TargetChain::Index,
InitializationData<SourceChain::Header>,
) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
) {
let result = do_initialize(
source_client,
target_client,
target_transactions_signer,
prepare_initialize_transaction,
)
.await;
match result {
Ok(Some(tx_hash)) => log::info!(
target: "bridge",
"Successfully submitted {}-headers bridge initialization transaction to {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
tx_hash,
),
Ok(None) => (),
Err(err) => log::error!(
target: "bridge",
"Failed to submit {}-headers bridge initialization transaction to {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
err,
),
}
}
/// Craft and submit initialization transaction, returning any error that may occur.
async fn do_initialize<SourceChain: ChainWithGrandpa, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
target_transactions_signer: TargetChain::AccountId,
prepare_initialize_transaction: impl FnOnce(
TargetChain::Index,
InitializationData<SourceChain::Header>,
) -> Result<Bytes, SubstrateError>
+ Send
+ 'static,
) -> Result<
Option<TargetChain::Hash>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
> {
let is_initialized = is_initialized::<SourceChain, TargetChain>(&target_client).await?;
if is_initialized {
log::info!(
target: "bridge",
"{}-headers bridge at {} is already initialized. Skipping",
SourceChain::NAME,
TargetChain::NAME,
);
return Ok(None)
}
let initialization_data = prepare_initialization_data(source_client).await?;
log::info!(
target: "bridge",
"Prepared initialization data for {}-headers bridge at {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
initialization_data,
);
let initialization_tx_hash = target_client
.submit_signed_extrinsic(target_transactions_signer, move |_, transaction_nonce| {
prepare_initialize_transaction(transaction_nonce, initialization_data)
})
.await
.map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?;
Ok(Some(initialization_tx_hash))
}
/// Returns `Ok(true)` if bridge has already been initialized.
async fn is_initialized<SourceChain: ChainWithGrandpa, TargetChain: Chain>(
target_client: &Client<TargetChain>,
) -> Result<bool, Error<HashOf<SourceChain>, BlockNumberOf<SourceChain>>> {
Ok(target_client
.raw_storage_value(
bp_header_chain::storage_keys::best_finalized_hash_key(
SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME,
),
None,
)
.await
.map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))?
.is_some())
}
/// Prepare initialization data for the GRANDPA verifier pallet.
async fn prepare_initialization_data<SourceChain: Chain>(
source_client: Client<SourceChain>,
) -> Result<
InitializationData<SourceChain::Header>,
Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
> {
// 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_justifications()
.await
.map_err(|err| Error::Subscribe(SourceChain::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(SourceChain::NAME, e))
.and_then(|justification| {
justification.ok_or(Error::ReadJustificationStreamEnded(SourceChain::NAME))
})?;
// Read initial header.
let justification: GrandpaJustification<SourceChain::Header> =
Decode::decode(&mut &justification.0[..])
.map_err(|err| Error::DecodeJustification(SourceChain::NAME, err))?;
let (initial_header_hash, initial_header_number) =
(justification.commit.target_hash, justification.commit.target_number);
let initial_header = source_header(&source_client, initial_header_hash).await?;
log::trace!(target: "bridge", "Selected {} initial header: {}/{}",
SourceChain::NAME,
initial_header_number,
initial_header_hash,
);
// Read GRANDPA authorities set at initial header.
let initial_authorities_set =
source_authorities_set(&source_client, initial_header_hash).await?;
log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}",
SourceChain::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 =
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: {:?}",
SourceChain::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 = SourceChain::BlockNumber::zero();
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone())
.ok_or(Error::ReadInvalidAuthorities(SourceChain::NAME, authorities_for_verification))?;
loop {
log::trace!(
target: "bridge", "Trying {} GRANDPA authorities set id: {}",
SourceChain::NAME,
initial_authorities_set_id,
);
let is_valid_set_id = verify_justification::<SourceChain::Header>(
(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(SourceChain::NAME, initial_header_number))
}
}
Ok(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,
})
}
/// Read header by hash from the source client.
async fn source_header<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<SourceChain::Header, Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>>
{
source_client
.header_by_hash(header_hash)
.await
.map_err(|err| Error::RetrieveHeader(SourceChain::NAME, header_hash, err))
}
/// Read GRANDPA authorities set at given header.
async fn source_authorities_set<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<GrandpaAuthoritiesSet, Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>>
{
let raw_authorities_set = source_client
.grandpa_authorities_set(header_hash)
.await
.map_err(|err| Error::RetrieveAuthorities(SourceChain::NAME, header_hash, err))?;
GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..])
.map_err(|err| Error::DecodeAuthorities(SourceChain::NAME, header_hash, err))
}
@@ -22,11 +22,7 @@ use std::time::Duration;
pub mod conversion_rate_update;
pub mod error;
pub mod finality_guards;
pub mod finality_pipeline;
pub mod finality_source;
pub mod finality_target;
pub mod headers_initialize;
pub mod finality;
pub mod helpers;
pub mod messages_lane;
pub mod messages_metrics;
@@ -30,9 +30,11 @@ use relay_utils::{
};
use crate::{
finality_pipeline::{SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT},
finality_source::{RequiredHeaderNumberRef, SubstrateFinalitySource},
finality_target::SubstrateFinalityTarget,
finality::{
source::{RequiredHeaderNumberRef, SubstrateFinalitySource},
target::SubstrateFinalityTarget,
SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT,
},
TransactionParams, STALL_TIMEOUT,
};