substrate_relay_helper/finality/
target.rs1use crate::{
20 finality::{
21 FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
22 },
23 finality_base::{best_synced_header_id, engine::Engine, SubstrateFinalityProof},
24 TransactionParams,
25};
26
27use async_trait::async_trait;
28use bp_runtime::BlockNumberOf;
29use finality_relay::TargetClient;
30use relay_substrate_client::{
31 AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SyncHeader,
32 TransactionEra, TransactionTracker, UnsignedTransaction,
33};
34use relay_utils::relay_loop::Client as RelayClient;
35use sp_core::Pair;
36use sp_runtime::traits::Header;
37
38pub struct SubstrateFinalityTarget<P: SubstrateFinalitySyncPipeline, TargetClnt> {
40 client: TargetClnt,
41 transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
42}
43
44impl<P: SubstrateFinalitySyncPipeline, TargetClnt: Client<P::TargetChain>>
45 SubstrateFinalityTarget<P, TargetClnt>
46{
47 pub fn new(
49 client: TargetClnt,
50 transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
51 ) -> Self {
52 SubstrateFinalityTarget { client, transaction_params }
53 }
54
55 pub async fn ensure_pallet_active(&self) -> Result<(), Error> {
57 let is_halted = P::FinalityEngine::is_halted(&self.client).await?;
58 if is_halted {
59 return Err(Error::BridgePalletIsHalted)
60 }
61
62 let is_initialized = P::FinalityEngine::is_initialized(&self.client).await?;
63 if !is_initialized {
64 return Err(Error::BridgePalletIsNotInitialized)
65 }
66
67 Ok(())
68 }
69}
70
71impl<P: SubstrateFinalitySyncPipeline, TargetClnt: Clone> Clone
72 for SubstrateFinalityTarget<P, TargetClnt>
73{
74 fn clone(&self) -> Self {
75 SubstrateFinalityTarget {
76 client: self.client.clone(),
77 transaction_params: self.transaction_params.clone(),
78 }
79 }
80}
81
82#[async_trait]
83impl<P: SubstrateFinalitySyncPipeline, TargetClnt: Client<P::TargetChain>> RelayClient
84 for SubstrateFinalityTarget<P, TargetClnt>
85{
86 type Error = Error;
87
88 async fn reconnect(&mut self) -> Result<(), Error> {
89 self.client.reconnect().await
90 }
91}
92
93#[async_trait]
94impl<P: SubstrateFinalitySyncPipeline, TargetClnt: Client<P::TargetChain>>
95 TargetClient<FinalitySyncPipelineAdapter<P>> for SubstrateFinalityTarget<P, TargetClnt>
96where
97 AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TargetChain> as Pair>::Public>,
98{
99 type TransactionTracker = TransactionTracker<P::TargetChain, TargetClnt>;
100
101 async fn best_finalized_source_block_id(&self) -> Result<HeaderIdOf<P::SourceChain>, Error> {
102 self.client.ensure_synced().await?;
105 self.ensure_pallet_active().await?;
107
108 Ok(best_synced_header_id::<P::SourceChain, P::TargetChain>(
109 &self.client,
110 self.client.best_header().await?.hash(),
111 )
112 .await?
113 .ok_or(Error::BridgePalletIsNotInitialized)?)
114 }
115
116 async fn free_source_headers_interval(
117 &self,
118 ) -> Result<Option<BlockNumberOf<P::SourceChain>>, Self::Error> {
119 Ok(self
120 .client
121 .state_call(
122 self.client.best_header().await?.hash(),
123 P::SourceChain::FREE_HEADERS_INTERVAL_METHOD.into(),
124 (),
125 )
126 .await
127 .unwrap_or_else(|e| {
128 log::info!(
129 target: "bridge",
130 "Call of {} at {} has failed with an error: {:?}. Treating as `None`",
131 P::SourceChain::FREE_HEADERS_INTERVAL_METHOD,
132 P::TargetChain::NAME,
133 e,
134 );
135 None
136 }))
137 }
138
139 async fn submit_finality_proof(
140 &self,
141 header: SyncHeader<HeaderOf<P::SourceChain>>,
142 mut proof: SubstrateFinalityProof<P>,
143 is_free_execution_expected: bool,
144 ) -> Result<Self::TransactionTracker, Error> {
145 let context =
147 P::FinalityEngine::verify_and_optimize_proof(&self.client, &header, &mut proof).await?;
148
149 if is_free_execution_expected {
153 let extras = P::FinalityEngine::check_max_expected_call_limits(&header, &proof);
154 if extras.is_weight_limit_exceeded || extras.extra_size != 0 {
155 return Err(Error::FinalityProofWeightLimitExceeded { extras })
156 }
157 }
158
159 let mortality = self.transaction_params.mortality;
161 let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(
162 header,
163 proof,
164 is_free_execution_expected,
165 context,
166 );
167 self.client
168 .submit_and_watch_signed_extrinsic(
169 &self.transaction_params.signer,
170 move |best_block_id, transaction_nonce| {
171 Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
172 .era(TransactionEra::new(best_block_id, mortality)))
173 },
174 )
175 .await
176 }
177}