mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 07:31:02 +00:00
Complex RialtoParachain <> Millau relay (#1405)
* complex parachain relay * fix spelling
This commit is contained in:
committed by
Bastian Köcher
parent
5f2f61ced5
commit
542ebb5654
@@ -0,0 +1,110 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Types and functions intended to ease adding of new Substrate -> Substrate
|
||||
//! parachain finality proofs synchronization pipelines.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
|
||||
use pallet_bridge_parachains::{
|
||||
Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash,
|
||||
RelayBlockHasher, RelayBlockNumber,
|
||||
};
|
||||
use parachains_relay::ParachainsPipeline;
|
||||
use relay_substrate_client::{CallOf, Chain, HashOf, RelayChain, TransactionSignScheme};
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
pub mod source;
|
||||
pub mod target;
|
||||
|
||||
/// Substrate -> Substrate parachain finality proofs synchronization pipeline.
|
||||
///
|
||||
/// This is currently restricted to the single parachain, because it is how it
|
||||
/// will be used (at least) initially.
|
||||
#[async_trait]
|
||||
pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync {
|
||||
/// Headers of this parachain are submitted to the `Self::TargetChain`.
|
||||
type SourceParachain: Chain;
|
||||
/// Relay chain that is storing headers of `Self::SourceParachain`.
|
||||
type SourceRelayChain: RelayChain;
|
||||
/// Target chain where `Self::SourceParachain` headers are submitted.
|
||||
type TargetChain: Chain;
|
||||
|
||||
/// How submit parachains heads call is built?
|
||||
type SubmitParachainHeadsCallBuilder: SubmitParachainHeadsCallBuilder<Self>;
|
||||
/// Scheme used to sign target chain transactions.
|
||||
type TransactionSignScheme: TransactionSignScheme;
|
||||
|
||||
/// Id of the `Self::SourceParachain`, used for registration in `Self::SourceRelayChain`.
|
||||
const SOURCE_PARACHAIN_PARA_ID: u32;
|
||||
}
|
||||
|
||||
/// Adapter that allows all `SubstrateParachainsPipeline` to act as `ParachainsPipeline`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParachainsPipelineAdapter<P: SubstrateParachainsPipeline> {
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateParachainsPipeline> ParachainsPipeline for ParachainsPipelineAdapter<P> {
|
||||
type SourceChain = P::SourceRelayChain;
|
||||
type TargetChain = P::TargetChain;
|
||||
}
|
||||
|
||||
/// Different ways of building `submit_parachain_heads` calls.
|
||||
pub trait SubmitParachainHeadsCallBuilder<P: SubstrateParachainsPipeline>:
|
||||
'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<P::SourceRelayChain>,
|
||||
parachains: Vec<ParaId>,
|
||||
parachain_heads_proof: ParaHeadsProof,
|
||||
) -> CallOf<P::TargetChain>;
|
||||
}
|
||||
|
||||
/// Building `submit_parachain_heads` call when you have direct access to the target
|
||||
/// chain runtime.
|
||||
pub struct DirectSubmitParachainHeadsCallBuilder<P, R, I> {
|
||||
_phantom: PhantomData<(P, R, I)>,
|
||||
}
|
||||
|
||||
impl<P, R, I> SubmitParachainHeadsCallBuilder<P> for DirectSubmitParachainHeadsCallBuilder<P, R, I>
|
||||
where
|
||||
P: SubstrateParachainsPipeline,
|
||||
P::SourceRelayChain: Chain<Hash = RelayBlockHash>,
|
||||
R: BridgeParachainsConfig<I> + Send + Sync,
|
||||
I: 'static + Send + Sync,
|
||||
R::BridgedChain: bp_runtime::Chain<
|
||||
BlockNumber = RelayBlockNumber,
|
||||
Hash = RelayBlockHash,
|
||||
Hasher = RelayBlockHasher,
|
||||
>,
|
||||
CallOf<P::TargetChain>: From<BridgeParachainsCall<R, I>>,
|
||||
{
|
||||
fn build_submit_parachain_heads_call(
|
||||
relay_block_hash: HashOf<P::SourceRelayChain>,
|
||||
parachains: Vec<ParaId>,
|
||||
parachain_heads_proof: ParaHeadsProof,
|
||||
) -> CallOf<P::TargetChain> {
|
||||
BridgeParachainsCall::<R, I>::submit_parachain_heads {
|
||||
relay_block_hash,
|
||||
parachains,
|
||||
parachain_heads_proof,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Parachain heads source.
|
||||
|
||||
use crate::{
|
||||
finality::source::RequiredHeaderNumberRef,
|
||||
parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline},
|
||||
};
|
||||
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
use async_trait::async_trait;
|
||||
use bp_parachains::parachain_head_storage_key_at_source;
|
||||
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
|
||||
use codec::Decode;
|
||||
use parachains_relay::parachains_loop::SourceClient;
|
||||
use relay_substrate_client::{
|
||||
Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, RelayChain,
|
||||
};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use sp_runtime::traits::Header as HeaderT;
|
||||
|
||||
/// Substrate client as parachain heads source.
|
||||
#[derive(Clone)]
|
||||
pub struct ParachainsSource<P: SubstrateParachainsPipeline> {
|
||||
client: Client<P::SourceRelayChain>,
|
||||
maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceParachain>>,
|
||||
previous_parachain_head: Arc<Mutex<Option<ParaHash>>>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateParachainsPipeline> ParachainsSource<P> {
|
||||
/// Creates new parachains source client.
|
||||
pub fn new(
|
||||
client: Client<P::SourceRelayChain>,
|
||||
maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceParachain>>,
|
||||
) -> Self {
|
||||
let previous_parachain_head = Arc::new(Mutex::new(None));
|
||||
ParachainsSource { client, maximal_header_number, previous_parachain_head }
|
||||
}
|
||||
|
||||
/// Returns reference to the underlying RPC client.
|
||||
pub fn client(&self) -> &Client<P::SourceRelayChain> {
|
||||
&self.client
|
||||
}
|
||||
|
||||
/// Return decoded head of given parachain.
|
||||
pub async fn on_chain_parachain_header(
|
||||
&self,
|
||||
at_block: HeaderIdOf<P::SourceRelayChain>,
|
||||
para_id: ParaId,
|
||||
) -> Result<Option<HeaderOf<P::SourceParachain>>, SubstrateError> {
|
||||
let storage_key =
|
||||
parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, para_id);
|
||||
let para_head = self.client.raw_storage_value(storage_key, Some(at_block.1)).await?;
|
||||
let para_head = para_head.map(|h| ParaHead::decode(&mut &h.0[..])).transpose()?;
|
||||
let para_head = match para_head {
|
||||
Some(para_head) => para_head,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some(Decode::decode(&mut ¶_head.0[..])?))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateParachainsPipeline> RelayClient for ParachainsSource<P> {
|
||||
type Error = SubstrateError;
|
||||
|
||||
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
|
||||
self.client.reconnect().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateParachainsPipeline> SourceClient<ParachainsPipelineAdapter<P>>
|
||||
for ParachainsSource<P>
|
||||
where
|
||||
P::SourceParachain: Chain<Hash = ParaHash>,
|
||||
{
|
||||
async fn ensure_synced(&self) -> Result<bool, Self::Error> {
|
||||
match self.client.ensure_synced().await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(SubstrateError::ClientNotSynced(_)) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn parachain_head(
|
||||
&self,
|
||||
at_block: HeaderIdOf<P::SourceRelayChain>,
|
||||
para_id: ParaId,
|
||||
) -> Result<Option<ParaHash>, Self::Error> {
|
||||
// we don't need to support many parachains now
|
||||
if para_id.0 != P::SOURCE_PARACHAIN_PARA_ID {
|
||||
return Err(SubstrateError::Custom(format!(
|
||||
"Parachain id {} is not matching expected {}",
|
||||
para_id.0,
|
||||
P::SOURCE_PARACHAIN_PARA_ID,
|
||||
)))
|
||||
}
|
||||
|
||||
let parachain_head = match self.on_chain_parachain_header(at_block, para_id).await? {
|
||||
Some(parachain_header) => {
|
||||
let mut parachain_head = Some(parachain_header.hash());
|
||||
// never return head that is larger than requested. This way we'll never sync
|
||||
// headers past `maximal_header_number`
|
||||
if let Some(ref maximal_header_number) = self.maximal_header_number {
|
||||
let maximal_header_number = *maximal_header_number.lock().await;
|
||||
if *parachain_header.number() > maximal_header_number {
|
||||
let previous_parachain_head = *self.previous_parachain_head.lock().await;
|
||||
if let Some(previous_parachain_head) = previous_parachain_head {
|
||||
parachain_head = Some(previous_parachain_head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parachain_head
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
*self.previous_parachain_head.lock().await = parachain_head;
|
||||
|
||||
Ok(parachain_head)
|
||||
}
|
||||
|
||||
async fn prove_parachain_heads(
|
||||
&self,
|
||||
at_block: HeaderIdOf<P::SourceRelayChain>,
|
||||
parachains: &[ParaId],
|
||||
) -> Result<ParaHeadsProof, Self::Error> {
|
||||
let storage_keys = parachains
|
||||
.iter()
|
||||
.map(|para_id| {
|
||||
parachain_head_storage_key_at_source(
|
||||
P::SourceRelayChain::PARAS_PALLET_NAME,
|
||||
*para_id,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let parachain_heads_proof = self
|
||||
.client
|
||||
.prove_storage(storage_keys, at_block.1)
|
||||
.await?
|
||||
.iter_nodes()
|
||||
.collect();
|
||||
|
||||
Ok(parachain_heads_proof)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Parachain heads target.
|
||||
|
||||
use crate::{
|
||||
parachains::{
|
||||
ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline,
|
||||
},
|
||||
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 parachains_relay::parachains_loop::TargetClient;
|
||||
use relay_substrate_client::{
|
||||
AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf,
|
||||
HeaderIdOf, RelayChain, 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;
|
||||
|
||||
/// Substrate client as parachain heads source.
|
||||
pub struct ParachainsTarget<P: SubstrateParachainsPipeline> {
|
||||
client: Client<P::TargetChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
|
||||
}
|
||||
|
||||
impl<P: SubstrateParachainsPipeline> ParachainsTarget<P> {
|
||||
/// Creates new parachains target client.
|
||||
pub fn new(
|
||||
client: Client<P::TargetChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
|
||||
) -> Self {
|
||||
ParachainsTarget { client, transaction_params }
|
||||
}
|
||||
|
||||
/// Returns reference to the underlying RPC client.
|
||||
pub fn client(&self) -> &Client<P::TargetChain> {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: SubstrateParachainsPipeline> Clone for ParachainsTarget<P> {
|
||||
fn clone(&self) -> Self {
|
||||
ParachainsTarget {
|
||||
client: self.client.clone(),
|
||||
transaction_params: self.transaction_params.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: SubstrateParachainsPipeline> RelayClient for ParachainsTarget<P> {
|
||||
type Error = SubstrateError;
|
||||
|
||||
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
|
||||
self.client.reconnect().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P> TargetClient<ParachainsPipelineAdapter<P>> for ParachainsTarget<P>
|
||||
where
|
||||
P: SubstrateParachainsPipeline,
|
||||
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
|
||||
AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TransactionSignScheme> as Pair>::Public>,
|
||||
{
|
||||
async fn best_block(&self) -> Result<HeaderIdOf<P::TargetChain>, 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<P::TargetChain>,
|
||||
) -> Result<HeaderIdOf<P::SourceRelayChain>, Self::Error> {
|
||||
let encoded_best_finalized_source_block = self
|
||||
.client
|
||||
.state_call(
|
||||
P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(),
|
||||
Bytes(Vec::new()),
|
||||
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)?;
|
||||
Ok(HeaderId(decoded_best_finalized_source_block.0, decoded_best_finalized_source_block.1))
|
||||
}
|
||||
|
||||
async fn parachain_head(
|
||||
&self,
|
||||
at_block: HeaderIdOf<P::TargetChain>,
|
||||
para_id: ParaId,
|
||||
) -> Result<Option<BestParaHeadHash>, Self::Error> {
|
||||
let storage_key = parachain_head_storage_key_at_target(
|
||||
P::SourceRelayChain::PARACHAINS_FINALITY_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<P::SourceRelayChain>,
|
||||
updated_parachains: Vec<ParaId>,
|
||||
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 = P::SubmitParachainHeadsCallBuilder::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(
|
||||
P::TransactionSignScheme::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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user