// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Parachain heads target. use crate::TransactionParams; use async_trait::async_trait; use bp_parachains::{parachain_head_storage_key_at_target, BestParaHeadHash}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; use codec::{Decode, Encode}; use pallet_bridge_parachains::{ Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, }; use parachains_relay::{parachains_loop::TargetClient, ParachainsPipeline}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf, SignParam, TransactionEra, TransactionSignScheme, UnsignedTransaction, }; use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; use sp_core::{Bytes, Pair}; use sp_runtime::traits::Header as HeaderT; use std::marker::PhantomData; /// Different ways of building `submit_parachain_heads` calls. pub trait SubmitParachainHeadsCallBuilder: 'static + Send + Sync { /// Given parachains and their heads proof, build call of `submit_parachain_heads` /// function of bridge parachains module at the target chain. fn build_submit_parachain_heads_call( relay_block_hash: HashOf, parachains: Vec, parachain_heads_proof: ParaHeadsProof, ) -> CallOf; } /// Building `submit_parachain_heads` call when you have direct access to the target /// chain runtime. pub struct DirectSubmitParachainHeadsCallBuilder { _phantom: PhantomData<(P, R, I)>, } impl SubmitParachainHeadsCallBuilder

for DirectSubmitParachainHeadsCallBuilder where P: ParachainsPipeline, P::SourceChain: Chain, R: BridgeParachainsConfig + Send + Sync, I: 'static + Send + Sync, R::BridgedChain: bp_runtime::Chain< BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher, >, CallOf: From>, { fn build_submit_parachain_heads_call( relay_block_hash: HashOf, parachains: Vec, parachain_heads_proof: ParaHeadsProof, ) -> CallOf { BridgeParachainsCall::::submit_parachain_heads { relay_block_hash, parachains, parachain_heads_proof, } .into() } } /// Substrate client as parachain heads source. pub struct ParachainsTarget { client: Client, transaction_params: TransactionParams>, bridge_paras_pallet_name: String, _phantom: PhantomData, } impl ParachainsTarget { /// Creates new parachains target client. pub fn new( client: Client, transaction_params: TransactionParams>, bridge_paras_pallet_name: String, ) -> Self { ParachainsTarget { client, transaction_params, bridge_paras_pallet_name, _phantom: Default::default(), } } } impl Clone for ParachainsTarget { fn clone(&self) -> Self { ParachainsTarget { client: self.client.clone(), transaction_params: self.transaction_params.clone(), bridge_paras_pallet_name: self.bridge_paras_pallet_name.clone(), _phantom: Default::default(), } } } #[async_trait] impl< P: ParachainsPipeline, S: 'static + TransactionSignScheme, CB: SubmitParachainHeadsCallBuilder

, > RelayClient for ParachainsTarget { type Error = SubstrateError; async fn reconnect(&mut self) -> Result<(), SubstrateError> { self.client.reconnect().await } } #[async_trait] impl TargetClient

for ParachainsTarget where P: ParachainsPipeline, S: 'static + TransactionSignScheme, CB: SubmitParachainHeadsCallBuilder

, AccountIdOf: From< as Pair>::Public>, { async fn best_block(&self) -> Result, Self::Error> { let best_header = self.client.best_header().await?; let best_hash = best_header.hash(); let best_id = HeaderId(*best_header.number(), best_hash); Ok(best_id) } async fn best_finalized_source_block( &self, at_block: &HeaderIdOf, ) -> Result, Self::Error> { let encoded_best_finalized_source_block = self .client .state_call( P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), Bytes(Vec::new()), Some(at_block.1), ) .await?; let decoded_best_finalized_source_block: ( BlockNumberOf, HashOf, ) = Decode::decode(&mut &encoded_best_finalized_source_block.0[..]) .map_err(SubstrateError::ResponseParseFailed)?; Ok(HeaderId(decoded_best_finalized_source_block.0, decoded_best_finalized_source_block.1)) } async fn parachain_head( &self, at_block: HeaderIdOf, para_id: ParaId, ) -> Result, Self::Error> { let storage_key = parachain_head_storage_key_at_target(&self.bridge_paras_pallet_name, para_id); let para_head = self.client.storage_value(storage_key, Some(at_block.1)).await?; Ok(para_head) } async fn submit_parachain_heads_proof( &self, at_relay_block: HeaderIdOf, updated_parachains: Vec, proof: ParaHeadsProof, ) -> Result<(), Self::Error> { let genesis_hash = *self.client.genesis_hash(); let transaction_params = self.transaction_params.clone(); let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; let call = CB::build_submit_parachain_heads_call(at_relay_block.1, updated_parachains, proof); self.client .submit_signed_extrinsic( self.transaction_params.signer.public().into(), move |best_block_id, transaction_nonce| { Ok(Bytes( S::sign_transaction(SignParam { spec_version, transaction_version, genesis_hash, signer: transaction_params.signer, era: TransactionEra::new(best_block_id, transaction_params.mortality), unsigned: UnsignedTransaction::new(call.into(), transaction_nonce), })? .encode(), )) }, ) .await .map(drop) } }