substrate_relay_helper/parachains/
target.rs1use crate::{
20 parachains::{
21 ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline,
22 },
23 TransactionParams,
24};
25
26use async_trait::async_trait;
27use bp_parachains::{
28 ImportedParaHeadsKeyProvider, ParaInfo, ParaStoredHeaderData, ParasInfoKeyProvider,
29};
30use bp_polkadot_core::{
31 parachains::{ParaHash, ParaHeadsProof, ParaId},
32 BlockNumber as RelayBlockNumber,
33};
34use bp_runtime::{
35 Chain as ChainBase, HeaderId, HeaderIdProvider, StorageDoubleMapKeyProvider,
36 StorageMapKeyProvider,
37};
38use parachains_relay::parachains_loop::TargetClient;
39use relay_substrate_client::{
40 AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError,
41 HeaderIdOf, ParachainBase, RelayChain, TransactionEra, TransactionTracker, UnsignedTransaction,
42};
43use relay_utils::relay_loop::Client as RelayClient;
44use sp_core::Pair;
45use sp_runtime::traits::Header;
46
47pub struct ParachainsTarget<P: SubstrateParachainsPipeline, SourceClnt, TargetClnt> {
49 source_client: SourceClnt,
50 target_client: TargetClnt,
51 transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
52}
53
54impl<
55 P: SubstrateParachainsPipeline,
56 SourceClnt: Client<P::SourceRelayChain>,
57 TargetClnt: Client<P::TargetChain>,
58 > ParachainsTarget<P, SourceClnt, TargetClnt>
59{
60 pub fn new(
62 source_client: SourceClnt,
63 target_client: TargetClnt,
64 transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
65 ) -> Self {
66 ParachainsTarget { source_client, target_client, transaction_params }
67 }
68
69 pub fn target_client(&self) -> &TargetClnt {
71 &self.target_client
72 }
73}
74
75impl<
76 P: SubstrateParachainsPipeline,
77 SourceClnt: Client<P::SourceRelayChain>,
78 TargetClnt: Clone,
79 > Clone for ParachainsTarget<P, SourceClnt, TargetClnt>
80{
81 fn clone(&self) -> Self {
82 ParachainsTarget {
83 source_client: self.source_client.clone(),
84 target_client: self.target_client.clone(),
85 transaction_params: self.transaction_params.clone(),
86 }
87 }
88}
89
90#[async_trait]
91impl<
92 P: SubstrateParachainsPipeline,
93 SourceClnt: Client<P::SourceRelayChain>,
94 TargetClnt: Client<P::TargetChain>,
95 > RelayClient for ParachainsTarget<P, SourceClnt, TargetClnt>
96{
97 type Error = SubstrateError;
98
99 async fn reconnect(&mut self) -> Result<(), SubstrateError> {
100 self.target_client.reconnect().await?;
101 self.source_client.reconnect().await?;
102 Ok(())
103 }
104}
105
106#[async_trait]
107impl<P, SourceClnt, TargetClnt> TargetClient<ParachainsPipelineAdapter<P>>
108 for ParachainsTarget<P, SourceClnt, TargetClnt>
109where
110 P: SubstrateParachainsPipeline,
111 SourceClnt: Client<P::SourceRelayChain>,
112 TargetClnt: Client<P::TargetChain>,
113 AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TargetChain> as Pair>::Public>,
114 P::SourceParachain: ChainBase<Hash = ParaHash>,
115 P::SourceRelayChain: ChainBase<BlockNumber = RelayBlockNumber>,
116{
117 type TransactionTracker = TransactionTracker<P::TargetChain, TargetClnt>;
118
119 async fn best_block(&self) -> Result<HeaderIdOf<P::TargetChain>, Self::Error> {
120 let best_header = self.target_client.best_header().await?;
121 let best_id = best_header.id();
122
123 Ok(best_id)
124 }
125
126 async fn best_finalized_source_relay_chain_block(
127 &self,
128 at_block: &HeaderIdOf<P::TargetChain>,
129 ) -> Result<HeaderIdOf<P::SourceRelayChain>, Self::Error> {
130 self.target_client
131 .state_call::<_, Option<HeaderIdOf<P::SourceRelayChain>>>(
132 at_block.hash(),
133 P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(),
134 (),
135 )
136 .await?
137 .map(Ok)
138 .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized))
139 }
140
141 async fn free_source_relay_headers_interval(
142 &self,
143 ) -> Result<Option<BlockNumberOf<P::SourceRelayChain>>, Self::Error> {
144 Ok(self
145 .target_client
146 .state_call(
147 self.target_client.best_header().await?.hash(),
148 P::SourceRelayChain::FREE_HEADERS_INTERVAL_METHOD.into(),
149 (),
150 )
151 .await
152 .unwrap_or_else(|e| {
153 log::info!(
154 target: "bridge",
155 "Call of {} at {} has failed with an error: {:?}. Treating as `None`",
156 P::SourceRelayChain::FREE_HEADERS_INTERVAL_METHOD,
157 P::TargetChain::NAME,
158 e,
159 );
160 None
161 }))
162 }
163
164 async fn parachain_head(
165 &self,
166 at_block: HeaderIdOf<P::TargetChain>,
167 ) -> Result<
168 Option<(HeaderIdOf<P::SourceRelayChain>, HeaderIdOf<P::SourceParachain>)>,
169 Self::Error,
170 > {
171 let storage_key = ParasInfoKeyProvider::final_key(
173 P::SourceRelayChain::WITH_CHAIN_BRIDGE_PARACHAINS_PALLET_NAME,
174 &P::SourceParachain::PARACHAIN_ID.into(),
175 );
176 let storage_value: Option<ParaInfo> =
177 self.target_client.storage_value(at_block.hash(), storage_key).await?;
178 let para_info = match storage_value {
179 Some(para_info) => para_info,
180 None => return Ok(None),
181 };
182
183 let relay_header_id = self
186 .source_client
187 .header_by_number(para_info.best_head_hash.at_relay_block_number)
188 .await?
189 .id();
190
191 let storage_key = ImportedParaHeadsKeyProvider::final_key(
193 P::SourceRelayChain::WITH_CHAIN_BRIDGE_PARACHAINS_PALLET_NAME,
194 &P::SourceParachain::PARACHAIN_ID.into(),
195 ¶_info.best_head_hash.head_hash,
196 );
197 let storage_value: Option<ParaStoredHeaderData> =
198 self.target_client.storage_value(at_block.hash(), storage_key).await?;
199 let para_head_number = match storage_value {
200 Some(para_head_data) =>
201 para_head_data.decode_parachain_head_data::<P::SourceParachain>()?.number,
202 None => return Ok(None),
203 };
204
205 let para_head_id = HeaderId(para_head_number, para_info.best_head_hash.head_hash);
206 Ok(Some((relay_header_id, para_head_id)))
207 }
208
209 async fn submit_parachain_head_proof(
210 &self,
211 at_relay_block: HeaderIdOf<P::SourceRelayChain>,
212 updated_head_hash: ParaHash,
213 proof: ParaHeadsProof,
214 is_free_execution_expected: bool,
215 ) -> Result<Self::TransactionTracker, Self::Error> {
216 let transaction_params = self.transaction_params.clone();
217 let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call(
218 at_relay_block,
219 vec![(ParaId(P::SourceParachain::PARACHAIN_ID), updated_head_hash)],
220 proof,
221 is_free_execution_expected,
222 );
223 self.target_client
224 .submit_and_watch_signed_extrinsic(
225 &transaction_params.signer,
226 move |best_block_id, transaction_nonce| {
227 Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
228 .era(TransactionEra::new(best_block_id, transaction_params.mortality)))
229 },
230 )
231 .await
232 }
233}