From 31a2be845cc44f50d75340a494d2c16b5ac3a044 Mon Sep 17 00:00:00 2001
From: Svyatoslav Nikolsky <svyatonik@gmail.com>
Date: Tue, 21 Jun 2022 14:52:07 +0300
Subject: [PATCH] Fix on-different-forks metrics during initialization (#1468)

* fix on-different-forks metrics during initialization

* "initialize" parachain finality pallet in on-demand parachains relay

* decrease converstion rate requests count

* more error logging

* fix compilation

* clippy
---
 bridges/README.md                             |  2 +-
 bridges/bin/millau/runtime/src/lib.rs         | 23 ++---
 .../bin/rialto-parachain/runtime/src/lib.rs   |  5 +-
 bridges/bin/rialto/runtime/src/lib.rs         |  5 +-
 bridges/modules/grandpa/src/lib.rs            | 16 +--
 bridges/primitives/chain-kusama/src/lib.rs    |  2 +-
 bridges/primitives/chain-millau/src/lib.rs    |  2 +-
 bridges/primitives/chain-polkadot/src/lib.rs  |  2 +-
 .../chain-rialto-parachain/src/lib.rs         |  2 +-
 bridges/primitives/chain-rialto/src/lib.rs    |  2 +-
 bridges/primitives/chain-rococo/src/lib.rs    |  2 +-
 bridges/primitives/chain-westend/src/lib.rs   |  2 +-
 bridges/primitives/chain-wococo/src/lib.rs    |  2 +-
 bridges/primitives/header-chain/src/lib.rs    |  6 +-
 bridges/relays/client-substrate/src/client.rs | 20 +++-
 .../relays/lib-substrate-relay/src/error.rs   |  5 +
 .../src/finality/engine.rs                    | 19 +++-
 .../src/finality/initialize.rs                | 21 +---
 .../src/finality/target.rs                    |  9 +-
 .../src/messages_source.rs                    | 14 ++-
 .../src/messages_target.rs                    |  4 +
 .../src/on_demand/headers.rs                  |  2 +-
 .../src/on_demand/parachains.rs               | 97 ++++++++++++++-----
 .../src/parachains/target.rs                  | 12 ++-
 .../utils/src/metrics/float_json_value.rs     |  2 +-
 25 files changed, 173 insertions(+), 105 deletions(-)

diff --git a/bridges/README.md b/bridges/README.md
index 3d3b177bbaa..e2192e3d678 100644
--- a/bridges/README.md
+++ b/bridges/README.md
@@ -216,7 +216,7 @@ To run a Rialto node for example, you can use the following command:
 
 ```bash
 docker run -p 30333:30333 -p 9933:9933 -p 9944:9944 \
-  -it paritytech/rialto-bridge-node --dev --tmp \
+  -it local/rialto-bridge-node --dev --tmp \
   --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external
 ```
 
diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs
index 5195f9b48a2..c5c002c4721 100644
--- a/bridges/bin/millau/runtime/src/lib.rs
+++ b/bridges/bin/millau/runtime/src/lib.rs
@@ -795,33 +795,28 @@ impl_runtime_apis! {
 	}
 
 	impl bp_rialto::RialtoFinalityApi<Block> for Runtime {
-		fn best_finalized() -> (bp_rialto::BlockNumber, bp_rialto::Hash) {
-			let header = BridgeRialtoGrandpa::best_finalized();
-			(header.number, header.hash())
+		fn best_finalized() -> Option<(bp_rialto::BlockNumber, bp_rialto::Hash)> {
+			BridgeRialtoGrandpa::best_finalized().map(|header| (header.number, header.hash()))
 		}
 	}
 
 	impl bp_westend::WestendFinalityApi<Block> for Runtime {
-		fn best_finalized() -> (bp_westend::BlockNumber, bp_westend::Hash) {
-			let header = BridgeWestendGrandpa::best_finalized();
-			(header.number, header.hash())
+		fn best_finalized() -> Option<(bp_westend::BlockNumber, bp_westend::Hash)> {
+			BridgeWestendGrandpa::best_finalized().map(|header| (header.number, header.hash()))
 		}
 	}
 
 	impl bp_rialto_parachain::RialtoParachainFinalityApi<Block> for Runtime {
-		fn best_finalized() -> (bp_rialto::BlockNumber, bp_rialto::Hash) {
+		fn best_finalized() -> Option<(bp_rialto::BlockNumber, bp_rialto::Hash)> {
 			// the parachains finality pallet is never decoding parachain heads, so it is
 			// only done in the integration code
 			use bp_rialto_parachain::RIALTO_PARACHAIN_ID;
-			let best_rialto_parachain_head = pallet_bridge_parachains::Pallet::<
+			let encoded_head = pallet_bridge_parachains::Pallet::<
 				Runtime,
 				WithRialtoParachainsInstance,
-			>::best_parachain_head(RIALTO_PARACHAIN_ID.into())
-				.and_then(|encoded_header| bp_rialto_parachain::Header::decode(&mut &encoded_header.0[..]).ok());
-			match best_rialto_parachain_head {
-				Some(head) => (*head.number(), head.hash()),
-				None => (Default::default(), Default::default()),
-			}
+			>::best_parachain_head(RIALTO_PARACHAIN_ID.into())?;
+			let head = bp_rialto_parachain::Header::decode(&mut &encoded_head.0[..]).ok()?;
+			Some((*head.number(), head.hash()))
 		}
 	}
 
diff --git a/bridges/bin/rialto-parachain/runtime/src/lib.rs b/bridges/bin/rialto-parachain/runtime/src/lib.rs
index ec1c754f7df..0927f138f3b 100644
--- a/bridges/bin/rialto-parachain/runtime/src/lib.rs
+++ b/bridges/bin/rialto-parachain/runtime/src/lib.rs
@@ -683,9 +683,8 @@ impl_runtime_apis! {
 	}
 
 	impl bp_millau::MillauFinalityApi<Block> for Runtime {
-		fn best_finalized() -> (bp_millau::BlockNumber, bp_millau::Hash) {
-			let header = BridgeMillauGrandpa::best_finalized();
-			(header.number, header.hash())
+		fn best_finalized() -> Option<(bp_millau::BlockNumber, bp_millau::Hash)> {
+			BridgeMillauGrandpa::best_finalized().map(|header| (header.number, header.hash()))
 		}
 	}
 
diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs
index 9b7dc70394a..e10ddda0fb3 100644
--- a/bridges/bin/rialto/runtime/src/lib.rs
+++ b/bridges/bin/rialto/runtime/src/lib.rs
@@ -640,9 +640,8 @@ impl_runtime_apis! {
 	}
 
 	impl bp_millau::MillauFinalityApi<Block> for Runtime {
-		fn best_finalized() -> (bp_millau::BlockNumber, bp_millau::Hash) {
-			let header = BridgeMillauGrandpa::best_finalized();
-			(header.number, header.hash())
+		fn best_finalized() -> Option<(bp_millau::BlockNumber, bp_millau::Hash)> {
+			BridgeMillauGrandpa::best_finalized().map(|header| (header.number, header.hash()))
 		}
 	}
 
diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs
index 8fa0ed89cca..684905f2886 100644
--- a/bridges/modules/grandpa/src/lib.rs
+++ b/bridges/modules/grandpa/src/lib.rs
@@ -537,17 +537,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	///
 	/// Returns a dummy header if there is no best header. This can only happen
 	/// if the pallet has not been initialized yet.
-	pub fn best_finalized() -> BridgedHeader<T, I> {
+	pub fn best_finalized() -> Option<BridgedHeader<T, I>> {
 		let hash = <BestFinalized<T, I>>::get();
-		<ImportedHeaders<T, I>>::get(hash).unwrap_or_else(|| {
-			<BridgedHeader<T, I>>::new(
-				Default::default(),
-				Default::default(),
-				Default::default(),
-				Default::default(),
-				Default::default(),
-			)
-		})
+		<ImportedHeaders<T, I>>::get(hash)
 	}
 
 	/// Check if a particular header is known to the bridge pallet.
@@ -718,7 +710,7 @@ mod tests {
 				BestFinalized::<TestRuntime>::get(),
 				BridgedBlockHash::<TestRuntime, ()>::default()
 			);
-			assert_eq!(Pallet::<TestRuntime>::best_finalized(), test_header(0));
+			assert_eq!(Pallet::<TestRuntime>::best_finalized(), None);
 
 			let init_data = init_with_origin(Origin::root()).unwrap();
 
@@ -1131,7 +1123,7 @@ mod tests {
 		run_test(|| {
 			initialize_substrate_bridge();
 			assert_ok!(submit_finality_proof(1));
-			let first_header = Pallet::<TestRuntime>::best_finalized();
+			let first_header = Pallet::<TestRuntime>::best_finalized().unwrap();
 			next_block();
 
 			assert_ok!(submit_finality_proof(2));
diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/primitives/chain-kusama/src/lib.rs
index 880fe5ac1bd..637f6b9da03 100644
--- a/bridges/primitives/chain-kusama/src/lib.rs
+++ b/bridges/primitives/chain-kusama/src/lib.rs
@@ -117,7 +117,7 @@ sp_api::decl_runtime_apis! {
 	/// Kusama runtime itself.
 	pub trait KusamaFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Kusama chain.
diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs
index c9b4153b2fc..240c2daaa98 100644
--- a/bridges/primitives/chain-millau/src/lib.rs
+++ b/bridges/primitives/chain-millau/src/lib.rs
@@ -314,7 +314,7 @@ sp_api::decl_runtime_apis! {
 	/// Millau runtime itself.
 	pub trait MillauFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Millau chain.
diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/primitives/chain-polkadot/src/lib.rs
index e16a23e1d14..f06bb917edc 100644
--- a/bridges/primitives/chain-polkadot/src/lib.rs
+++ b/bridges/primitives/chain-polkadot/src/lib.rs
@@ -118,7 +118,7 @@ sp_api::decl_runtime_apis! {
 	/// Polkadot runtime itself.
 	pub trait PolkadotFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Polkadot chain.
diff --git a/bridges/primitives/chain-rialto-parachain/src/lib.rs b/bridges/primitives/chain-rialto-parachain/src/lib.rs
index df66996c4b1..38b4192bf34 100644
--- a/bridges/primitives/chain-rialto-parachain/src/lib.rs
+++ b/bridges/primitives/chain-rialto-parachain/src/lib.rs
@@ -241,7 +241,7 @@ sp_api::decl_runtime_apis! {
 	/// RialtoParachain runtime itself.
 	pub trait RialtoParachainFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to RialtoParachain chain.
diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs
index 9d598b8d22f..e5c5ebab76f 100644
--- a/bridges/primitives/chain-rialto/src/lib.rs
+++ b/bridges/primitives/chain-rialto/src/lib.rs
@@ -262,7 +262,7 @@ sp_api::decl_runtime_apis! {
 	/// Rialto runtime itself.
 	pub trait RialtoFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Rialto chain.
diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs
index a0f545a95c2..1c2bfb42d0f 100644
--- a/bridges/primitives/chain-rococo/src/lib.rs
+++ b/bridges/primitives/chain-rococo/src/lib.rs
@@ -113,7 +113,7 @@ sp_api::decl_runtime_apis! {
 	/// Rococo runtime itself.
 	pub trait RococoFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Rococo chain.
diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs
index c7ebe4b00fd..539cf69eb73 100644
--- a/bridges/primitives/chain-westend/src/lib.rs
+++ b/bridges/primitives/chain-westend/src/lib.rs
@@ -106,6 +106,6 @@ sp_api::decl_runtime_apis! {
 	/// Westend runtime itself.
 	pub trait WestendFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 }
diff --git a/bridges/primitives/chain-wococo/src/lib.rs b/bridges/primitives/chain-wococo/src/lib.rs
index 69039355923..6e0bad12274 100644
--- a/bridges/primitives/chain-wococo/src/lib.rs
+++ b/bridges/primitives/chain-wococo/src/lib.rs
@@ -70,7 +70,7 @@ sp_api::decl_runtime_apis! {
 	/// Wococo runtime itself.
 	pub trait WococoFinalityApi {
 		/// Returns number and hash of the best finalized header known to the bridge module.
-		fn best_finalized() -> (BlockNumber, Hash);
+		fn best_finalized() -> Option<(BlockNumber, Hash)>;
 	}
 
 	/// Outbound message lane API for messages that are sent to Wococo chain.
diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs
index 29d9cc52544..ff8ee82f41e 100644
--- a/bridges/primitives/header-chain/src/lib.rs
+++ b/bridges/primitives/header-chain/src/lib.rs
@@ -88,7 +88,7 @@ pub trait InclusionProofVerifier {
 /// A trait for pallets which want to keep track of finalized headers from a bridged chain.
 pub trait HeaderChain<H, E> {
 	/// Get the best finalized header known to the header chain.
-	fn best_finalized() -> H;
+	fn best_finalized() -> Option<H>;
 
 	/// Get the best authority set known to the header chain.
 	fn authority_set() -> AuthoritySet;
@@ -98,8 +98,8 @@ pub trait HeaderChain<H, E> {
 }
 
 impl<H: Default, E> HeaderChain<H, E> for () {
-	fn best_finalized() -> H {
-		H::default()
+	fn best_finalized() -> Option<H> {
+		None
 	}
 
 	fn authority_set() -> AuthoritySet {
diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs
index bb9f8230056..448c33c5ba3 100644
--- a/bridges/relays/client-substrate/src/client.rs
+++ b/bridges/relays/client-substrate/src/client.rs
@@ -456,8 +456,12 @@ impl<C: Chain> Client<C> {
 				IndexOf<C>,
 				C::SignedBlock,
 			>::author_submit_extrinsic(&*client, transaction)
-			.await?;
-			log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash);
+			.await
+			.map_err(|e| {
+				log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e);
+				e
+			})?;
+			log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash);
 			Ok(tx_hash)
 		})
 		.await
@@ -499,7 +503,11 @@ impl<C: Chain> Client<C> {
 				IndexOf<C>,
 				C::SignedBlock,
 			>::author_submit_extrinsic(&*client, extrinsic)
-			.await?;
+			.await
+			.map_err(|e| {
+				log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e);
+				e
+			})?;
 			log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash);
 			Ok(tx_hash)
 		})
@@ -528,7 +536,11 @@ impl<C: Chain> Client<C> {
 							.map_err(|e| Error::RpcError(e.into()))?])),
 						"author_unwatchExtrinsic",
 					)
-					.await?;
+					.await
+					.map_err(|e| {
+						log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e);
+						e
+					})?;
 				log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash);
 				Ok(subscription)
 			})
diff --git a/bridges/relays/lib-substrate-relay/src/error.rs b/bridges/relays/lib-substrate-relay/src/error.rs
index 9402d55e379..5accba4fc7c 100644
--- a/bridges/relays/lib-substrate-relay/src/error.rs
+++ b/bridges/relays/lib-substrate-relay/src/error.rs
@@ -58,4 +58,9 @@ pub enum Error<Hash: Debug + MaybeDisplay, HeaderNumber: Debug + MaybeDisplay> {
 	/// Failed to retrieve best finalized source header hash from the target chain.
 	#[error("Failed to retrieve best finalized {0} header from the target chain: {1}")]
 	RetrieveBestFinalizedHeaderHash(&'static str, client::Error),
+	/// Failed to submit signed extrinsic from to the target chain.
+	#[error(
+		"Failed to retrieve `is_initialized` flag of the with-{0} finality pallet at {1}: {2:?}"
+	)]
+	IsInitializedRetrieve(&'static str, &'static str, client::Error),
 }
diff --git a/bridges/relays/lib-substrate-relay/src/finality/engine.rs b/bridges/relays/lib-substrate-relay/src/finality/engine.rs
index 0b20a3222d2..76038fcb976 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/engine.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/engine.rs
@@ -37,7 +37,7 @@ use std::marker::PhantomData;
 
 /// Finality enfine, used by the Substrate chain.
 #[async_trait]
-pub trait Engine<C: Chain> {
+pub trait Engine<C: Chain>: Send {
 	/// Unique consensus engine identifier.
 	const ID: ConsensusEngineId;
 	/// Type of finality proofs, used by consensus engine.
@@ -59,6 +59,23 @@ pub trait Engine<C: Chain> {
 	async fn prepare_initialization_data(
 		client: Client<C>,
 	) -> Result<Self::InitializationData, Error<HashOf<C>, BlockNumberOf<C>>>;
+
+	/// Returns `Ok(true)` if finality pallet at the bridged chain has already been initialized.
+	async fn is_initialized<TargetChain: Chain>(
+		target_client: &Client<TargetChain>,
+	) -> Result<bool, SubstrateError> {
+		Ok(target_client
+			.raw_storage_value(Self::is_initialized_key(), None)
+			.await?
+			.is_some())
+	}
+
+	/// Returns `Ok(true)` if finality pallet at the bridged chain is halted.
+	async fn is_halted<TargetChain: Chain>(
+		target_client: &Client<TargetChain>,
+	) -> Result<bool, SubstrateError> {
+		Ok(target_client.storage_value(Self::is_halted_key(), None).await?.unwrap_or(false))
+	}
 }
 
 /// GRANDPA finality engine.
diff --git a/bridges/relays/lib-substrate-relay/src/finality/initialize.rs b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs
index 2fc9302d90f..be1719b4ae0 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/initialize.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs
@@ -23,7 +23,7 @@
 
 use crate::{error::Error, finality::engine::Engine};
 
-use relay_substrate_client::{BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf};
+use relay_substrate_client::{Chain, Client, Error as SubstrateError};
 use sp_core::Bytes;
 use sp_runtime::traits::Header as HeaderT;
 
@@ -80,7 +80,9 @@ where
 		+ Send
 		+ 'static,
 {
-	let is_initialized = is_initialized::<E, SourceChain, TargetChain>(&target_client).await?;
+	let is_initialized = E::is_initialized(&target_client)
+		.await
+		.map_err(|e| Error::IsInitializedRetrieve(SourceChain::NAME, TargetChain::NAME, e))?;
 	if is_initialized {
 		log::info!(
 			target: "bridge",
@@ -108,18 +110,3 @@ where
 		.map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?;
 	Ok(Some(initialization_tx_hash))
 }
-
-/// Returns `Ok(true)` if bridge has already been initialized.
-pub(crate) 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())
-}
diff --git a/bridges/relays/lib-substrate-relay/src/finality/target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs
index 12098a74799..d04fe280e5a 100644
--- a/bridges/relays/lib-substrate-relay/src/finality/target.rs
+++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs
@@ -51,15 +51,12 @@ impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> {
 
 	/// 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(P::FinalityEngine::is_halted_key(), None).await?;
-		if is_halted.unwrap_or(false) {
+		let is_halted = P::FinalityEngine::is_halted(&self.client).await?;
+		if is_halted {
 			return Err(Error::BridgePalletIsHalted)
 		}
 
-		let is_initialized =
-			super::initialize::is_initialized::<P::FinalityEngine, P::SourceChain, P::TargetChain>(
-				&self.client,
-			)
+		let is_initialized = P::FinalityEngine::is_initialized(&self.client)
 			.await
 			.map_err(|e| Error::Custom(e.to_string()))?;
 		if !is_initialized {
diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs
index 85bd9ea7d0f..0e21ef66eda 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_source.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs
@@ -146,7 +146,11 @@ where
 	async fn state(&self) -> Result<SourceClientState<MessageLaneAdapter<P>>, SubstrateError> {
 		// we can't continue to deliver confirmations if source node is out of sync, because
 		// it may have already received confirmations that we're going to deliver
+		//
+		// we can't continue to deliver messages if target node is out of sync, because
+		// it may have already received (some of) messages that we're going to deliver
 		self.source_client.ensure_synced().await?;
+		self.target_client.ensure_synced().await?;
 		// we can't relay confirmations if messages pallet at source chain is halted
 		self.ensure_pallet_active().await?;
 
@@ -562,9 +566,13 @@ where
 			Some(at_self_hash),
 		)
 		.await?;
-	let decoded_best_finalized_peer_on_self: (BlockNumberOf<PeerChain>, HashOf<PeerChain>) =
-		Decode::decode(&mut &encoded_best_finalized_peer_on_self.0[..])
-			.map_err(SubstrateError::ResponseParseFailed)?;
+	let decoded_best_finalized_peer_on_self =
+		Option::<(BlockNumberOf<PeerChain>, HashOf<PeerChain>)>::decode(
+			&mut &encoded_best_finalized_peer_on_self.0[..],
+		)
+		.map_err(SubstrateError::ResponseParseFailed)?
+		.map(Ok)
+		.unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized))?;
 	let peer_on_self_best_finalized_id =
 		HeaderId(decoded_best_finalized_peer_on_self.0, decoded_best_finalized_peer_on_self.1);
 
diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs
index 00c0c157054..d860fa68a93 100644
--- a/bridges/relays/lib-substrate-relay/src/messages_target.rs
+++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs
@@ -146,8 +146,12 @@ where
 	BalanceOf<P::SourceChain>: TryFrom<BalanceOf<P::TargetChain>>,
 {
 	async fn state(&self) -> Result<TargetClientState<MessageLaneAdapter<P>>, SubstrateError> {
+		// we can't continue to deliver confirmations if source node is out of sync, because
+		// it may have already received confirmations that we're going to deliver
+		//
 		// we can't continue to deliver messages if target node is out of sync, because
 		// it may have already received (some of) messages that we're going to deliver
+		self.source_client.ensure_synced().await?;
 		self.target_client.ensure_synced().await?;
 		// we can't relay messages if messages pallet at target chain is halted
 		self.ensure_pallet_active().await?;
diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
index f22ad96b636..749205ef9b3 100644
--- a/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
+++ b/bridges/relays/lib-substrate-relay/src/on_demand/headers.rs
@@ -253,7 +253,7 @@ async fn background_task<P: SubstrateFinalitySyncPipeline>(
 
 			log::info!(
 				target: "bridge",
-				"[{}] Starting on-demand relay task\n\t\
+				"[{}] Starting on-demand headers relay task\n\t\
 					Only mandatory headers: {}\n\t\
 					Tx mortality: {:?} (~{}m)\n\t\
 					Stall timeout: {:?}",
diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
index 579f3103e82..f25d3b76948 100644
--- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
+++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs
@@ -181,6 +181,7 @@ async fn background_task<P: SubstrateParachainsPipeline>(
 					new_required_parachain_header_number,
 				);
 			},
+			_ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {},
 			_ = parachains_relay_task => {
 				// this should never happen in practice given the current code
 				restart_relay = true;
@@ -266,7 +267,7 @@ async fn background_task<P: SubstrateParachainsPipeline>(
 
 			log::info!(
 				target: "bridge",
-				"[{}] Starting on-demand-relay task\n\t\
+				"[{}] Starting on-demand-parachains relay task\n\t\
 					Tx mortality: {:?} (~{}m)\n\t\
 					Stall timeout: {:?}",
 				relay_task_name,
@@ -317,7 +318,7 @@ struct RelayData<ParaHash, ParaNumber, RelayNumber> {
 	/// Parachain header number that is required at the target chain.
 	pub required_para_header: ParaNumber,
 	/// Parachain header number, known to the target chain.
-	pub para_header_at_target: ParaNumber,
+	pub para_header_at_target: Option<ParaNumber>,
 	/// Parachain header id, known to the source (relay) chain.
 	pub para_header_at_source: Option<HeaderId<ParaHash, ParaNumber>>,
 	/// Parachain header, that is available at the source relay chain at `relay_header_at_target`
@@ -374,9 +375,15 @@ where
 			best_target_block_hash,
 			P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD,
 		)
-		.await
-		.map_err(map_target_err)?
-		.0;
+		.await;
+	// if there are no parachain heads at the target (`BridgePalletIsNotInitialized`), we'll need
+	// to submit at least one. Otherwise the pallet will be treated as uninitialized and messages
+	// sync will stall.
+	let para_header_at_target = match para_header_at_target {
+		Ok(para_header_at_target) => Some(para_header_at_target.0),
+		Err(SubstrateError::BridgePalletIsNotInitialized) => None,
+		Err(e) => return Err(map_target_err(e)),
+	};
 
 	let best_finalized_relay_header =
 		source.client().best_finalized_header().await.map_err(map_source_err)?;
@@ -424,7 +431,7 @@ fn select_headers_to_relay<ParaHash, ParaNumber, RelayNumber>(
 ) -> RelayState<ParaHash, ParaNumber, RelayNumber>
 where
 	ParaHash: Clone,
-	ParaNumber: Copy + PartialOrd,
+	ParaNumber: Copy + PartialOrd + Zero,
 	RelayNumber: Copy + Debug + Ord,
 {
 	// this switch is responsible for processing `RelayingRelayHeader` state
@@ -450,30 +457,38 @@ where
 	}
 
 	// this switch is responsible for processing `RelayingParaHeader` state
+	let para_header_at_target_or_zero = data.para_header_at_target.unwrap_or_else(Zero::zero);
 	match state {
 		RelayState::Idle => (),
 		RelayState::RelayingRelayHeader(_) => unreachable!("processed by previous match; qed"),
 		RelayState::RelayingParaHeader(para_header_id) => {
-			if data.para_header_at_target < para_header_id.0 {
+			if para_header_at_target_or_zero < para_header_id.0 {
 				// parachain header hasn't yet been relayed
 				return RelayState::RelayingParaHeader(para_header_id)
 			}
 		},
 	}
 
-	// if we have already satisfied our "customer", do nothing
-	if data.required_para_header <= data.para_header_at_target {
-		return RelayState::Idle
-	}
-
 	// if we haven't read para head from the source, we can't yet do anyhting
 	let para_header_at_source = match data.para_header_at_source {
 		Some(ref para_header_at_source) => para_header_at_source.clone(),
 		None => return RelayState::Idle,
 	};
 
+	// if we have parachain head at the source, but no parachain heads at the target, we'll need
+	// to deliver at least one parachain head
+	let (required_para_header, para_header_at_target) = match data.para_header_at_target {
+		Some(para_header_at_target) => (data.required_para_header, para_header_at_target),
+		None => (para_header_at_source.0, Zero::zero()),
+	};
+
+	// if we have already satisfied our "customer", do nothing
+	if required_para_header <= para_header_at_target {
+		return RelayState::Idle
+	}
+
 	// if required header is not available even at the source chain, let's wait
-	if data.required_para_header > para_header_at_source.0 {
+	if required_para_header > para_header_at_source.0 {
 		return RelayState::Idle
 	}
 
@@ -499,7 +514,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 90,
-					para_header_at_target: 50,
+					para_header_at_target: Some(50),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 700,
@@ -517,7 +532,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 90,
-					para_header_at_target: 50,
+					para_header_at_target: Some(50),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 750,
@@ -535,7 +550,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 90,
-					para_header_at_target: 50,
+					para_header_at_target: Some(50),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -552,7 +567,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 90,
-					para_header_at_target: 50,
+					para_header_at_target: Some(50),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -570,7 +585,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 90,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -588,7 +603,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 120,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: None,
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -606,7 +621,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 120,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: Some(HeaderId(110, 110)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -624,7 +639,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 120,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: Some(HeaderId(125, 125)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 780,
@@ -642,7 +657,7 @@ mod tests {
 			select_headers_to_relay(
 				&RelayData {
 					required_para_header: 120,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: Some(HeaderId(125, 125)),
 					relay_header_at_source: 800,
 					relay_header_at_target: 800,
@@ -660,7 +675,7 @@ mod tests {
 			select_headers_to_relay::<i32, _, _>(
 				&RelayData {
 					required_para_header: 120,
-					para_header_at_target: 105,
+					para_header_at_target: Some(105),
 					para_header_at_source: None,
 					relay_header_at_source: 800,
 					relay_header_at_target: 800,
@@ -671,4 +686,40 @@ mod tests {
 			RelayState::Idle,
 		);
 	}
+
+	#[test]
+	fn relay_starts_relaying_first_parachain_header() {
+		assert_eq!(
+			select_headers_to_relay::<i32, _, _>(
+				&RelayData {
+					required_para_header: 0,
+					para_header_at_target: None,
+					para_header_at_source: Some(HeaderId(125, 125)),
+					relay_header_at_source: 800,
+					relay_header_at_target: 800,
+					para_header_at_relay_header_at_target: Some(HeaderId(125, 125)),
+				},
+				RelayState::Idle,
+			),
+			RelayState::RelayingParaHeader(HeaderId(125, 125)),
+		);
+	}
+
+	#[test]
+	fn relay_starts_relaying_relay_header_to_relay_first_parachain_header() {
+		assert_eq!(
+			select_headers_to_relay::<i32, _, _>(
+				&RelayData {
+					required_para_header: 0,
+					para_header_at_target: None,
+					para_header_at_source: Some(HeaderId(125, 125)),
+					relay_header_at_source: 800,
+					relay_header_at_target: 700,
+					para_header_at_relay_header_at_target: Some(HeaderId(125, 125)),
+				},
+				RelayState::Idle,
+			),
+			RelayState::RelayingRelayHeader(800),
+		);
+	}
 }
diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs
index 9ff35f5a0c1..67d82e1f707 100644
--- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs
+++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs
@@ -102,11 +102,13 @@ where
 				Some(at_block.1),
 			)
 			.await?;
-		let decoded_best_finalized_source_block: (
-			BlockNumberOf<P::SourceRelayChain>,
-			HashOf<P::SourceRelayChain>,
-		) = Decode::decode(&mut &encoded_best_finalized_source_block.0[..])
-			.map_err(SubstrateError::ResponseParseFailed)?;
+		let decoded_best_finalized_source_block =
+			Option::<(BlockNumberOf<P::SourceRelayChain>, HashOf<P::SourceRelayChain>)>::decode(
+				&mut &encoded_best_finalized_source_block.0[..],
+			)
+			.map_err(SubstrateError::ResponseParseFailed)?
+			.map(Ok)
+			.unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized))?;
 		Ok(HeaderId(decoded_best_finalized_source_block.0, decoded_best_finalized_source_block.1))
 	}
 
diff --git a/bridges/relays/utils/src/metrics/float_json_value.rs b/bridges/relays/utils/src/metrics/float_json_value.rs
index 7535cbef986..17b09e05097 100644
--- a/bridges/relays/utils/src/metrics/float_json_value.rs
+++ b/bridges/relays/utils/src/metrics/float_json_value.rs
@@ -27,7 +27,7 @@ use async_trait::async_trait;
 use std::time::Duration;
 
 /// Value update interval.
-const UPDATE_INTERVAL: Duration = Duration::from_secs(60);
+const UPDATE_INTERVAL: Duration = Duration::from_secs(300);
 
 /// Metric that represents float value received from HTTP service as float gauge.
 ///
-- 
GitLab