diff --git a/bridges/README.md b/bridges/README.md index 3d3b177bbaae62c4d6d7ccc99ba5629a91983dae..e2192e3d6780f6c2e2e456ad1c42aecc878c893c 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 5195f9b48a2ae58fcd7ed74ea04b479f65308a8a..c5c002c472179a6da5842e931ace1d0147c37369 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 ec1c754f7dfdb8add2c93c85646f68e92fb3a02a..0927f138f3baf3b329e8b7e318053e0cc88ed80f 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 9b7dc70394ac52826b818c7332566e838e427d1a..e10ddda0fb3a91b9acc7a39740af74a7e65084f6 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 8fa0ed89cca2a2e741933832f5307d3bda922fcd..684905f2886062d60179253a8fc4c9c64a9d124e 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 880fe5ac1bd00189c6d89a35bef3d79235a55c1a..637f6b9da03f7a27e624df31f9aee064ae998d1a 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 c9b4153b2fc898efe291dd679fd8d4fec76235fb..240c2daaa98c16c1a475620e0554b065b38a2d15 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 e16a23e1d148f6fd1d9bc1c33368c9ea5a845d57..f06bb917edc718ac8f1ee52fe8abc3336854f070 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 df66996c4b133be4b53588756febe5d750c263c4..38b4192bf348c5fe37176d881bf6eda549d81504 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 9d598b8d22fedd49c0b4e288557e75caac67e95e..e5c5ebab76ff50f459ff11f88a1a4c809dbc83df 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 a0f545a95c21e4ecec8ca9c2c0cb4b533c54dc4a..1c2bfb42d0f0d0eeb7d615e8de9fc30a95ad324f 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 c7ebe4b00fd5d2c90b46ad1b90c9e60ed7e3347f..539cf69eb73a53e6b3d4db4c70bc929d1d1b8eeb 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 69039355923c4be537ca8f3aae27b0bdf6e01b1a..6e0bad122740408513a7c9b2b653448256e10f6a 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 29d9cc525445d09a2ce5bec7da2e73c48febd0bc..ff8ee82f41e42631b4533674a38bb983c188b822 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 bb9f8230056687ea7834e939c984adc7e0c7cfb7..448c33c5ba3979d69cbec5ef5fca55633b72d1bc 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 9402d55e379815467d2f1586d9c09df806ee72cb..5accba4fc7ce586032b9d961d6223570520c0767 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 0b20a3222d232a1a818e8b46f5626cd92b59b52e..76038fcb976762492a999df2e1486fcab58fc8c7 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 2fc9302d90f587a2d25d2a811ac83967ecb39c39..be1719b4ae033e5b8f217d33a824acb9ce9cbe5b 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 12098a74799774b5e852603096fbcce59c295e8d..d04fe280e5a2f5dc456d43b9060275c9a5039835 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 85bd9ea7d0f7709fa5b2905215eb6a39be44dcda..0e21ef66eda3b8e22e328aaec101a9423a74a83a 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 00c0c1570541e9a6078536f9f08f3421ce606a00..d860fa68a934b1ac01893cb80bf901b035593375 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 f22ad96b636ea2e0513b200b90abb66e58154d48..749205ef9b3cd85e4fc1e454e829f8d51438ab59 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 579f3103e8222f2edc3bb4f69bfd9c5595309639..f25d3b76948c95fcbcbdee425ee188eb248de3c4 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 9ff35f5a0c1061096f989e484bdb27f5cf677a4d..67d82e1f7075f47dc3e2f5a4e238e9309387ba82 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 7535cbef9863f6bfc6372f5d7d23b53d5785408c..17b09e050973ada83a9735e373292c92ed31ff4b 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. ///