referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/parachains/
target.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Parachain heads target.
18
19use 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
47/// Substrate client as parachain heads source.
48pub 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	/// Creates new parachains target client.
61	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	/// Returns reference to the underlying RPC client.
70	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		// read best parachain head from the target bridge-parachains pallet
172		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		// now we need to get full header ids. For source relay chain it is simple, because we
184		// are connected
185		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		// for parachain, we need to read from the target chain runtime storage
192		let storage_key = ImportedParaHeadsKeyProvider::final_key(
193			P::SourceRelayChain::WITH_CHAIN_BRIDGE_PARACHAINS_PALLET_NAME,
194			&P::SourceParachain::PARACHAIN_ID.into(),
195			&para_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}