Relay Millau && Rialto headers using (future) finality verifier API (#761)

* finality proofs relay

* SyncHeader::is_mandatory

* empty ancestry proof

* logs

* fixed submit condition

* fixed wrong split index

* tick comment

* recent_finality_proofs

* basic finality loop tests

* removed obsolete files

* rename files in substrate relay

* fmt

* clippy

* fixed TODOs

* clippy

* stop syncing if target node is out of sync

* more clippy

* more clippy

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* docs

* moved doc

* typo

* Update relays/finality-relay/src/finality_loop_tests.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update relays/finality-relay/src/finality_loop_tests.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* header_and_finality_proof_by_number -> header_and_finality_proof

* VecDeque isn't required (because of make_contiguous)

* fixed wrong expect

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update relays/substrate/src/rialto_headers_to_millau.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update relays/substrate/src/rialto_headers_to_millau.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* RialtoSyncHeader

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* removed wrong comment

* Update relays/finality-relay/src/finality_loop.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* fix used runtime methods names

* fix for new jsonrpsee

* fix comment

* initialize finality verifier pallet

* fmt

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Svyatoslav Nikolsky
2021-03-08 13:18:53 +03:00
committed by Bastian Köcher
parent e13ff320ea
commit f87053c1cb
31 changed files with 1490 additions and 944 deletions
+2 -1
View File
@@ -26,12 +26,13 @@ bp-polkadot = { path = "../../primitives/polkadot" }
bp-runtime = { path = "../../primitives/runtime" }
bp-rialto = { path = "../../primitives/rialto" }
bridge-runtime-common = { path = "../../bin/runtime-common" }
finality-relay = { path = "../finality-relay" }
headers-relay = { path = "../headers-relay" }
messages-relay = { path = "../messages-relay" }
millau-runtime = { path = "../../bin/millau/runtime" }
pallet-bridge-call-dispatch = { path = "../../modules/call-dispatch" }
pallet-finality-verifier = { path = "../../modules/finality-verifier" }
pallet-message-lane = { path = "../../modules/message-lane" }
pallet-substrate-bridge = { path = "../../modules/substrate" }
relay-kusama-client = { path = "../kusama-client" }
relay-millau-client = { path = "../millau-client" }
relay-polkadot-client = { path = "../polkadot-client" }
@@ -0,0 +1,130 @@
// 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/>.
//! Substrate-to-Substrate headers sync entrypoint.
use crate::finality_target::SubstrateFinalityTarget;
use async_trait::async_trait;
use codec::Encode;
use finality_relay::{FinalitySyncParams, FinalitySyncPipeline};
use relay_substrate_client::{
finality_source::{FinalitySource, Justification},
BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, SyncHeader,
};
use relay_utils::BlockNumberBase;
use std::{fmt::Debug, marker::PhantomData, time::Duration};
/// Default synchronization loop timeout.
const STALL_TIMEOUT: Duration = Duration::from_secs(120);
/// Default limit of recent finality proofs.
///
/// Finality delay of 4096 blocks is unlikely to happen in practice in
/// Substrate+GRANDPA based chains (good to know).
const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
/// Headers sync pipeline for Substrate <-> Substrate relays.
#[async_trait]
pub trait SubstrateFinalitySyncPipeline: FinalitySyncPipeline {
/// Name of the runtime method that returns id of best finalized source header at target chain.
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str;
/// Signed transaction type.
type SignedTransaction: Send + Sync + Encode;
/// Make submit header transaction.
async fn make_submit_finality_proof_transaction(
&self,
header: Self::Header,
proof: Self::FinalityProof,
) -> Result<Self::SignedTransaction, SubstrateError>;
}
/// Substrate-to-Substrate finality proof pipeline.
#[derive(Debug, Clone)]
pub struct SubstrateFinalityToSubstrate<SourceChain, TargetChain: Chain, TargetSign> {
/// Client for the target chain.
pub(crate) target_client: Client<TargetChain>,
/// Data required to sign target chain transactions.
pub(crate) target_sign: TargetSign,
/// Unused generic arguments dump.
_marker: PhantomData<SourceChain>,
}
impl<SourceChain, TargetChain: Chain, TargetSign> SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign> {
/// Create new Substrate-to-Substrate headers pipeline.
pub fn new(target_client: Client<TargetChain>, target_sign: TargetSign) -> Self {
SubstrateFinalityToSubstrate {
target_client,
target_sign,
_marker: Default::default(),
}
}
}
impl<SourceChain, TargetChain, TargetSign> FinalitySyncPipeline
for SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>
where
SourceChain: Clone + Chain + Debug,
BlockNumberOf<SourceChain>: BlockNumberBase,
TargetChain: Clone + Chain + Debug,
TargetSign: Clone + Send + Sync + Debug,
{
const SOURCE_NAME: &'static str = SourceChain::NAME;
const TARGET_NAME: &'static str = TargetChain::NAME;
type Hash = HashOf<SourceChain>;
type Number = BlockNumberOf<SourceChain>;
type Header = SyncHeader<SourceChain::Header>;
type FinalityProof = Justification<SourceChain::Header>;
}
/// Run Substrate-to-Substrate finality sync.
pub async fn run<SourceChain, TargetChain, P>(
pipeline: P,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
metrics_params: Option<relay_utils::metrics::MetricsParams>,
) where
P: SubstrateFinalitySyncPipeline<
Hash = HashOf<SourceChain>,
Number = BlockNumberOf<SourceChain>,
Header = SyncHeader<SourceChain::Header>,
FinalityProof = Justification<SourceChain::Header>,
>,
SourceChain: Clone + Chain,
BlockNumberOf<SourceChain>: BlockNumberBase,
TargetChain: Clone + Chain,
{
log::info!(
target: "bridge",
"Starting {} -> {} finality proof relay",
SourceChain::NAME,
TargetChain::NAME,
);
finality_relay::run(
FinalitySource::new(source_client),
SubstrateFinalityTarget::new(target_client, pipeline),
FinalitySyncParams {
tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: STALL_TIMEOUT,
},
metrics_params,
futures::future::pending(),
);
}
@@ -0,0 +1,91 @@
// Copyright 2019-2020 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/>.
//! Substrate client as Substrate finality proof target. The chain we connect to should have
//! runtime that implements `<BridgedChainName>FinalityApi` to allow bridging with
//! <BridgedName> chain.
use crate::finality_pipeline::SubstrateFinalitySyncPipeline;
use async_trait::async_trait;
use codec::{Decode, Encode};
use finality_relay::TargetClient;
use futures::TryFutureExt;
use relay_substrate_client::{Chain, Client, Error as SubstrateError};
use relay_utils::relay_loop::Client as RelayClient;
use sp_core::Bytes;
/// Substrate client as Substrate finality target.
pub struct SubstrateFinalityTarget<C: Chain, P> {
client: Client<C>,
pipeline: P,
}
impl<C: Chain, P> SubstrateFinalityTarget<C, P> {
/// Create new Substrate headers target.
pub fn new(client: Client<C>, pipeline: P) -> Self {
SubstrateFinalityTarget { client, pipeline }
}
}
impl<C: Chain, P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalityTarget<C, P> {
fn clone(&self) -> Self {
SubstrateFinalityTarget {
client: self.client.clone(),
pipeline: self.pipeline.clone(),
}
}
}
#[async_trait]
impl<C: Chain, P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<C, P> {
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
#[async_trait]
impl<C, P> TargetClient<P> for SubstrateFinalityTarget<C, P>
where
C: Chain,
P::Number: Decode,
P::Hash: Decode,
P: SubstrateFinalitySyncPipeline,
{
async fn best_finalized_source_block_number(&self) -> Result<P::Number, SubstrateError> {
// we can't continue to relay finality if target node is out of sync, because
// it may have already received (some of) headers that we're going to relay
self.client.ensure_synced().await?;
Ok(crate::messages_source::read_client_state::<C, P::Hash, P::Number>(
&self.client,
P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET,
)
.await?
.best_finalized_peer_at_best_self
.0)
}
async fn submit_finality_proof(&self, header: P::Header, proof: P::FinalityProof) -> Result<(), SubstrateError> {
self.pipeline
.make_submit_finality_proof_transaction(header, proof)
.and_then(|tx| self.client.submit_extrinsic(Bytes(tx.encode())))
.await
.map(drop)
}
}
@@ -17,12 +17,12 @@
//! Initialize Substrate -> Substrate headers bridge.
//!
//! Initialization is a transaction that calls `initialize()` function of the
//! `pallet-substrate-bridge` pallet. This transaction brings initial header
//! `pallet-finality-verifier` pallet. This transaction brings initial header
//! and authorities set from source to target chain. The headers sync starts
//! with this header.
use codec::Decode;
use pallet_substrate_bridge::InitializationData;
use pallet_finality_verifier::InitializationData;
use relay_substrate_client::{Chain, Client};
use sp_core::Bytes;
use sp_finality_grandpa::{AuthorityList as GrandpaAuthoritiesSet, SetId as GrandpaAuthoritiesSetId};
@@ -132,10 +132,6 @@ async fn prepare_initialization_data<SourceChain: Chain>(
header: initial_header,
authority_list: initial_authorities_set,
set_id: initial_authorities_set_id.unwrap_or(0),
// There may be multiple scheduled changes, so on real chains we should select proper
// moment, when there's nothing scheduled. On ephemeral (temporary) chains, it is ok to
// start with genesis.
scheduled_change: None,
is_halted: false,
})
}
@@ -1,458 +0,0 @@
// Copyright 2019-2020 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/>.
//! Substrate-to-Substrate headers synchronization maintain procedure.
//!
//! Regular headers synchronization only depends on persistent justifications
//! that are generated when authorities set changes. This happens rarely on
//! real-word chains. So some other way to finalize headers is required.
//!
//! Full nodes are listening to GRANDPA messages, so they may have track authorities
//! votes on their own. They're returning both persistent and ephemeral justifications
//! (justifications that are not stored in the database and not broadcasted over network)
//! throught `grandpa_subscribeJustifications` RPC subscription.
//!
//! The idea of this maintain procedure is that when we see justification that 'improves'
//! best finalized header on the target chain, we submit this justification to the target
//! node.
use crate::headers_pipeline::SubstrateHeadersSyncPipeline;
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use codec::{Decode, Encode};
use futures::future::{poll_fn, FutureExt, TryFutureExt};
use headers_relay::{
sync::HeadersSync,
sync_loop::SyncMaintain,
sync_types::{HeaderIdOf, HeaderStatus},
};
use relay_substrate_client::{Chain, Client, Error as SubstrateError, JustificationsSubscription};
use relay_utils::HeaderId;
use sp_core::Bytes;
use sp_runtime::{traits::Header as HeaderT, Justification};
use std::{collections::VecDeque, marker::PhantomData, task::Poll};
/// Substrate-to-Substrate headers synchronization maintain procedure.
pub struct SubstrateHeadersToSubstrateMaintain<P: SubstrateHeadersSyncPipeline, SourceChain: Chain, TargetChain: Chain>
{
pipeline: P,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
justifications: Arc<Mutex<Justifications<P>>>,
_marker: PhantomData<SourceChain>,
}
/// Future and already received justifications from the source chain.
struct Justifications<P: SubstrateHeadersSyncPipeline> {
/// Justifications stream. None if it hasn't been initialized yet, or it has been dropped
/// by the rpc library.
stream: Option<JustificationsSubscription>,
/// Justifications that we have read from the stream but have not sent to the
/// target node, because their targets were still not synced.
queue: VecDeque<(HeaderIdOf<P>, Justification)>,
}
impl<P: SubstrateHeadersSyncPipeline, SourceChain: Chain, TargetChain: Chain>
SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain>
{
/// Create new maintain procedure.
pub async fn new(pipeline: P, source_client: Client<SourceChain>, target_client: Client<TargetChain>) -> Self {
let justifications = subscribe_justifications(&source_client).await;
SubstrateHeadersToSubstrateMaintain {
pipeline,
source_client,
target_client,
justifications: Arc::new(Mutex::new(Justifications {
stream: justifications,
queue: VecDeque::new(),
})),
_marker: Default::default(),
}
}
}
#[async_trait]
impl<P: SubstrateHeadersSyncPipeline, SourceChain: Chain, TargetChain: Chain> Clone
for SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain>
{
fn clone(&self) -> Self {
SubstrateHeadersToSubstrateMaintain {
pipeline: self.pipeline.clone(),
source_client: self.source_client.clone(),
target_client: self.target_client.clone(),
justifications: self.justifications.clone(),
_marker: Default::default(),
}
}
}
#[async_trait]
impl<P, SourceChain, TargetChain> SyncMaintain<P> for SubstrateHeadersToSubstrateMaintain<P, SourceChain, TargetChain>
where
SourceChain: Chain,
<SourceChain::Header as HeaderT>::Number: Into<P::Number>,
<SourceChain::Header as HeaderT>::Hash: Into<P::Hash>,
TargetChain: Chain,
P::Number: Decode,
P::Hash: Decode,
P: SubstrateHeadersSyncPipeline<Completion = Justification, Extra = ()>,
{
async fn maintain(&self, sync: &mut HeadersSync<P>) {
// lock justifications before doing anything else
let mut justifications = match self.justifications.try_lock() {
Some(justifications) => justifications,
None => {
// this should never happen, as we use single-thread executor
log::warn!(target: "bridge", "Failed to acquire {} justifications lock", P::SOURCE_NAME);
return;
}
};
// we need to read best finalized header from the target node to be able to
// choose justification to submit
let best_finalized = match best_finalized_header_id::<P, _>(&self.target_client).await {
Ok(best_finalized) => best_finalized,
Err(error) => {
log::warn!(
target: "bridge",
"Failed to read best finalized {} block from maintain: {:?}",
P::SOURCE_NAME,
error,
);
return;
}
};
log::debug!(
target: "bridge",
"Read best finalized {} block from {}: {:?}",
P::SOURCE_NAME,
P::TARGET_NAME,
best_finalized,
);
// Select justification to submit to the target node. We're submitting at most one justification
// on every maintain call. So maintain rate directly affects finalization rate.
let (resubscribe, justification_to_submit) = poll_fn(|context| {
// read justifications from the stream and push to the queue
let resubscribe = !justifications.read_from_stream::<SourceChain::Header>(context);
// remove all obsolete justifications from the queue
remove_obsolete::<P>(&mut justifications.queue, best_finalized);
// select justification to submit
Poll::Ready((resubscribe, select_justification(&mut justifications.queue, sync)))
})
.await;
// if justifications subscription has been dropped, resubscribe
if resubscribe {
justifications.stream = subscribe_justifications(&self.source_client).await;
}
// finally - submit selected justification
if let Some((target, justification)) = justification_to_submit {
let submit_result = self
.pipeline
.make_complete_header_transaction(target, justification)
.and_then(|tx| self.target_client.submit_extrinsic(Bytes(tx.encode())))
.await;
match submit_result {
Ok(_) => log::debug!(
target: "bridge",
"Submitted justification received over {} subscription. Target: {:?}",
P::SOURCE_NAME,
target,
),
Err(error) => log::warn!(
target: "bridge",
"Failed to submit justification received over {} subscription for {:?}: {:?}",
P::SOURCE_NAME,
target,
error,
),
}
}
}
}
impl<P> Justifications<P>
where
P::Number: Decode,
P::Hash: Decode,
P: SubstrateHeadersSyncPipeline<Completion = Justification, Extra = ()>,
{
/// Read justifications from the subscription stream without blocking.
///
/// Returns `true` if justifications stream is still readable and `false` if it has been
/// dropped by the RPC crate && we need to resubscribe.
#[must_use]
fn read_from_stream<'a, SourceHeader>(&mut self, context: &mut std::task::Context<'a>) -> bool
where
SourceHeader: HeaderT,
SourceHeader::Number: Into<P::Number>,
SourceHeader::Hash: Into<P::Hash>,
{
let stream = match self.stream.as_mut() {
Some(stream) => stream,
None => return false,
};
loop {
let maybe_next_justification = stream.next();
futures::pin_mut!(maybe_next_justification);
let maybe_next_justification = maybe_next_justification.poll_unpin(context);
let justification = match maybe_next_justification {
Poll::Ready(justification) => justification,
Poll::Pending => return true,
};
let justification = match justification {
Some(justification) => justification,
None => {
log::warn!(
target: "bridge",
"{} justifications stream has been dropped. Will be trying to resubscribe",
P::SOURCE_NAME,
);
return false;
}
};
// decode justification target
let target = bp_header_chain::justification::decode_justification_target::<SourceHeader>(&justification);
let target = match target {
Ok((target_hash, target_number)) => HeaderId(target_number.into(), target_hash.into()),
Err(error) => {
log::warn!(
target: "bridge",
"Failed to decode justification from {} subscription: {:?}",
P::SOURCE_NAME,
error,
);
continue;
}
};
log::debug!(
target: "bridge",
"Received {} justification over subscription. Target: {:?}",
P::SOURCE_NAME,
target,
);
self.queue.push_back((target, justification.0));
}
}
}
/// Clean queue of all justifications that are justifying already finalized blocks.
fn remove_obsolete<P: SubstrateHeadersSyncPipeline>(
queue: &mut VecDeque<(HeaderIdOf<P>, Justification)>,
best_finalized: HeaderIdOf<P>,
) {
while queue
.front()
.map(|(target, _)| target.0 <= best_finalized.0)
.unwrap_or(false)
{
queue.pop_front();
}
}
/// Select appropriate justification that would improve best finalized block on target node.
///
/// It is assumed that the selected justification will be submitted to the target node. The
/// justification itself and all preceeding justifications are removed from the queue.
fn select_justification<P>(
queue: &mut VecDeque<(HeaderIdOf<P>, Justification)>,
sync: &mut HeadersSync<P>,
) -> Option<(HeaderIdOf<P>, Justification)>
where
P: SubstrateHeadersSyncPipeline<Completion = Justification>,
{
let mut selected_justification = None;
while let Some((target, justification)) = queue.pop_front() {
// if we're waiting for this justification, report it
if sync.headers().requires_completion_data(&target) {
sync.headers_mut().completion_response(&target, Some(justification));
// we won't submit previous justifications as we going to submit justification for
// next header
selected_justification = None;
// we won't submit next justifications as we need to submit previous justifications
// first
break;
}
// if we know that the header is already synced (it is known to the target node), let's
// select it for submission. We still may select better justification on the next iteration.
if sync.headers().status(&target) == HeaderStatus::Synced {
selected_justification = Some((target, justification));
continue;
}
// finally - return justification back to the queue
queue.push_back((target, justification));
break;
}
selected_justification
}
/// Returns best finalized source header on the target chain.
async fn best_finalized_header_id<P, C>(client: &Client<C>) -> Result<HeaderIdOf<P>, SubstrateError>
where
P: SubstrateHeadersSyncPipeline,
P::Number: Decode,
P::Hash: Decode,
C: Chain,
{
let call = P::FINALIZED_BLOCK_METHOD.into();
let data = Bytes(Vec::new());
let encoded_response = client.state_call(call, data, None).await?;
let decoded_response: (P::Number, P::Hash) =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
Ok(best_header_id)
}
/// Subscribe to justifications stream at source node.
async fn subscribe_justifications<C: Chain>(client: &Client<C>) -> Option<JustificationsSubscription> {
match client.subscribe_justifications().await {
Ok(source_justifications) => {
log::debug!(
target: "bridge",
"Successfully (re)subscribed to {} justifications",
C::NAME,
);
Some(source_justifications)
}
Err(error) => {
log::warn!(
target: "bridge",
"Failed to subscribe to {} justifications: {:?}",
C::NAME,
error,
);
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::headers_pipeline::sync_params;
use crate::millau_headers_to_rialto::MillauHeadersToRialto;
fn parent_hash(index: u8) -> bp_millau::Hash {
if index == 1 {
Default::default()
} else {
header(index - 1).hash()
}
}
fn header_hash(index: u8) -> bp_millau::Hash {
header(index).hash()
}
fn header(index: u8) -> bp_millau::Header {
bp_millau::Header::new(
index as _,
Default::default(),
Default::default(),
parent_hash(index),
Default::default(),
)
}
#[test]
fn obsolete_justifications_are_removed() {
let mut queue = vec![
(HeaderId(1, header_hash(1)), vec![1]),
(HeaderId(2, header_hash(2)), vec![2]),
(HeaderId(3, header_hash(3)), vec![3]),
]
.into_iter()
.collect();
remove_obsolete::<MillauHeadersToRialto>(&mut queue, HeaderId(2, header_hash(2)));
assert_eq!(
queue,
vec![(HeaderId(3, header_hash(3)), vec![3])]
.into_iter()
.collect::<VecDeque<_>>(),
);
}
#[test]
fn latest_justification_is_selected() {
let mut queue = vec![
(HeaderId(1, header_hash(1)), vec![1]),
(HeaderId(2, header_hash(2)), vec![2]),
(HeaderId(3, header_hash(3)), vec![3]),
]
.into_iter()
.collect();
let mut sync = HeadersSync::<MillauHeadersToRialto>::new(sync_params());
sync.headers_mut().header_response(header(1).into());
sync.headers_mut().header_response(header(2).into());
sync.headers_mut().header_response(header(3).into());
sync.target_best_header_response(HeaderId(2, header_hash(2)));
assert_eq!(
select_justification(&mut queue, &mut sync),
Some((HeaderId(2, header_hash(2)), vec![2])),
);
}
#[test]
fn required_justification_is_reported() {
let mut queue = vec![
(HeaderId(1, header_hash(1)), vec![1]),
(HeaderId(2, header_hash(2)), vec![2]),
(HeaderId(3, header_hash(3)), vec![3]),
]
.into_iter()
.collect();
let mut sync = HeadersSync::<MillauHeadersToRialto>::new(sync_params());
sync.headers_mut().header_response(header(1).into());
sync.headers_mut().header_response(header(2).into());
sync.headers_mut().header_response(header(3).into());
sync.headers_mut()
.incomplete_headers_response(vec![HeaderId(2, header_hash(2))].into_iter().collect());
sync.target_best_header_response(HeaderId(2, header_hash(2)));
assert_eq!(sync.headers_mut().header_to_complete(), None,);
assert_eq!(select_justification(&mut queue, &mut sync), None,);
assert_eq!(
sync.headers_mut().header_to_complete(),
Some((HeaderId(2, header_hash(2)), &vec![2])),
);
}
}
@@ -1,166 +0,0 @@
// Copyright 2019-2020 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/>.
//! Substrate-to-Substrate headers sync entrypoint.
use crate::{headers_maintain::SubstrateHeadersToSubstrateMaintain, headers_target::SubstrateHeadersTarget};
use async_trait::async_trait;
use codec::Encode;
use headers_relay::{
sync::{HeadersSyncParams, TargetTransactionMode},
sync_types::{HeaderIdOf, HeadersSyncPipeline, QueuedHeader, SourceHeader},
};
use relay_substrate_client::{
headers_source::HeadersSource, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf,
};
use relay_utils::BlockNumberBase;
use sp_runtime::Justification;
use std::marker::PhantomData;
/// Headers sync pipeline for Substrate <-> Substrate relays.
#[async_trait]
pub trait SubstrateHeadersSyncPipeline: HeadersSyncPipeline {
/// Name of the `best_block` runtime method.
const BEST_BLOCK_METHOD: &'static str;
/// Name of the `finalized_block` runtime method.
const FINALIZED_BLOCK_METHOD: &'static str;
/// Name of the `is_known_block` runtime method.
const IS_KNOWN_BLOCK_METHOD: &'static str;
/// Name of the `incomplete_headers` runtime method.
const INCOMPLETE_HEADERS_METHOD: &'static str;
/// Signed transaction type.
type SignedTransaction: Send + Sync + Encode;
/// Make submit header transaction.
async fn make_submit_header_transaction(
&self,
header: QueuedHeader<Self>,
) -> Result<Self::SignedTransaction, SubstrateError>;
/// Make completion transaction for the header.
async fn make_complete_header_transaction(
&self,
id: HeaderIdOf<Self>,
completion: Justification,
) -> Result<Self::SignedTransaction, SubstrateError>;
}
/// Substrate-to-Substrate headers pipeline.
#[derive(Debug, Clone)]
pub struct SubstrateHeadersToSubstrate<SourceChain, SourceSyncHeader, TargetChain: Chain, TargetSign> {
/// Client for the target chain.
pub(crate) target_client: Client<TargetChain>,
/// Data required to sign target chain transactions.
pub(crate) target_sign: TargetSign,
/// Unused generic arguments dump.
_marker: PhantomData<(SourceChain, SourceSyncHeader)>,
}
impl<SourceChain, SourceSyncHeader, TargetChain: Chain, TargetSign>
SubstrateHeadersToSubstrate<SourceChain, SourceSyncHeader, TargetChain, TargetSign>
{
/// Create new Substrate-to-Substrate headers pipeline.
pub fn new(target_client: Client<TargetChain>, target_sign: TargetSign) -> Self {
SubstrateHeadersToSubstrate {
target_client,
target_sign,
_marker: Default::default(),
}
}
}
impl<SourceChain, SourceSyncHeader, TargetChain, TargetSign> HeadersSyncPipeline
for SubstrateHeadersToSubstrate<SourceChain, SourceSyncHeader, TargetChain, TargetSign>
where
SourceChain: Clone + Chain,
BlockNumberOf<SourceChain>: BlockNumberBase,
SourceSyncHeader:
SourceHeader<HashOf<SourceChain>, BlockNumberOf<SourceChain>> + std::ops::Deref<Target = SourceChain::Header>,
TargetChain: Clone + Chain,
TargetSign: Clone + Send + Sync,
{
const SOURCE_NAME: &'static str = SourceChain::NAME;
const TARGET_NAME: &'static str = TargetChain::NAME;
type Hash = HashOf<SourceChain>;
type Number = BlockNumberOf<SourceChain>;
type Header = SourceSyncHeader;
type Extra = ();
type Completion = Justification;
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
source.header().encode().len()
}
}
/// Return sync parameters for Substrate-to-Substrate headers sync.
pub fn sync_params() -> HeadersSyncParams {
HeadersSyncParams {
max_future_headers_to_download: 32,
max_headers_in_submitted_status: 8,
max_headers_in_single_submit: 1,
max_headers_size_in_single_submit: 1024 * 1024,
prune_depth: 256,
target_tx_mode: TargetTransactionMode::Signed,
}
}
/// Run Substrate-to-Substrate headers sync.
pub async fn run<SourceChain, TargetChain, P>(
pipeline: P,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
metrics_params: Option<relay_utils::metrics::MetricsParams>,
) where
P: SubstrateHeadersSyncPipeline<
Hash = HashOf<SourceChain>,
Number = BlockNumberOf<SourceChain>,
Completion = Justification,
Extra = (),
>,
P::Header: SourceHeader<HashOf<SourceChain>, BlockNumberOf<SourceChain>>,
SourceChain: Clone + Chain,
SourceChain::Header: Into<P::Header>,
BlockNumberOf<SourceChain>: BlockNumberBase,
TargetChain: Clone + Chain,
{
let sync_maintain = SubstrateHeadersToSubstrateMaintain::<_, SourceChain, _>::new(
pipeline.clone(),
source_client.clone(),
target_client.clone(),
)
.await;
log::info!(
target: "bridge",
"Starting {} -> {} headers relay",
SourceChain::NAME,
TargetChain::NAME,
);
headers_relay::sync_loop::run(
HeadersSource::new(source_client),
SourceChain::AVERAGE_BLOCK_INTERVAL,
SubstrateHeadersTarget::new(target_client, pipeline),
TargetChain::AVERAGE_BLOCK_INTERVAL,
sync_maintain,
sync_params(),
metrics_params,
futures::future::pending(),
);
}
@@ -1,168 +0,0 @@
// Copyright 2019-2020 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/>.
//! Substrate client as Substrate headers target. The chain we connect to should have
//! runtime that implements `<BridgedChainName>HeaderApi` to allow bridging with
//! <BridgedName> chain.
use crate::headers_pipeline::SubstrateHeadersSyncPipeline;
use async_trait::async_trait;
use codec::{Decode, Encode};
use futures::TryFutureExt;
use headers_relay::{
sync_loop::TargetClient,
sync_types::{HeaderIdOf, QueuedHeader, SubmittedHeaders},
};
use relay_substrate_client::{Chain, Client, Error as SubstrateError};
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::Bytes;
use sp_runtime::Justification;
use std::collections::HashSet;
/// Substrate client as Substrate headers target.
pub struct SubstrateHeadersTarget<C: Chain, P> {
client: Client<C>,
pipeline: P,
}
impl<C: Chain, P> SubstrateHeadersTarget<C, P> {
/// Create new Substrate headers target.
pub fn new(client: Client<C>, pipeline: P) -> Self {
SubstrateHeadersTarget { client, pipeline }
}
}
impl<C: Chain, P: SubstrateHeadersSyncPipeline> Clone for SubstrateHeadersTarget<C, P> {
fn clone(&self) -> Self {
SubstrateHeadersTarget {
client: self.client.clone(),
pipeline: self.pipeline.clone(),
}
}
}
#[async_trait]
impl<C: Chain, P: SubstrateHeadersSyncPipeline> RelayClient for SubstrateHeadersTarget<C, P> {
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
#[async_trait]
impl<C, P> TargetClient<P> for SubstrateHeadersTarget<C, P>
where
C: Chain,
P::Number: Decode,
P::Hash: Decode + Encode,
P: SubstrateHeadersSyncPipeline<Completion = Justification, Extra = ()>,
{
async fn best_header_id(&self) -> Result<HeaderIdOf<P>, SubstrateError> {
// we can't continue to relay headers if target node is out of sync, because
// it may have already received (some of) headers that we're going to relay
self.client.ensure_synced().await?;
let call = P::BEST_BLOCK_METHOD.into();
let data = Bytes(Vec::new());
let encoded_response = self.client.state_call(call, data, None).await?;
let decoded_response: Vec<(P::Number, P::Hash)> =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
// If we parse an empty list of headers it means that bridge pallet has not been initalized
// yet. Otherwise we expect to always have at least one header.
decoded_response
.last()
.ok_or(SubstrateError::UninitializedBridgePallet)
.map(|(num, hash)| HeaderId(*num, *hash))
}
async fn is_known_header(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, bool), SubstrateError> {
let call = P::IS_KNOWN_BLOCK_METHOD.into();
let data = Bytes(id.1.encode());
let encoded_response = self.client.state_call(call, data, None).await?;
let is_known_block: bool =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, is_known_block))
}
async fn submit_headers(
&self,
mut headers: Vec<QueuedHeader<P>>,
) -> SubmittedHeaders<HeaderIdOf<P>, SubstrateError> {
debug_assert_eq!(
headers.len(),
1,
"Substrate pallet only supports single header / transaction"
);
let header = headers.remove(0);
let id = header.id();
let submit_transaction_result = self
.pipeline
.make_submit_header_transaction(header)
.and_then(|tx| self.client.submit_extrinsic(Bytes(tx.encode())))
.await;
match submit_transaction_result {
Ok(_) => SubmittedHeaders {
submitted: vec![id],
incomplete: Vec::new(),
rejected: Vec::new(),
fatal_error: None,
},
Err(error) => SubmittedHeaders {
submitted: Vec::new(),
incomplete: Vec::new(),
rejected: vec![id],
fatal_error: Some(error),
},
}
}
async fn incomplete_headers_ids(&self) -> Result<HashSet<HeaderIdOf<P>>, SubstrateError> {
let call = P::INCOMPLETE_HEADERS_METHOD.into();
let data = Bytes(Vec::new());
let encoded_response = self.client.state_call(call, data, None).await?;
let decoded_response: Vec<(P::Number, P::Hash)> =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let incomplete_headers = decoded_response
.into_iter()
.map(|(number, hash)| HeaderId(number, hash))
.collect();
Ok(incomplete_headers)
}
async fn complete_header(
&self,
id: HeaderIdOf<P>,
completion: Justification,
) -> Result<HeaderIdOf<P>, SubstrateError> {
let tx = self.pipeline.make_complete_header_transaction(id, completion).await?;
self.client.submit_extrinsic(Bytes(tx.encode())).await?;
Ok(id)
}
async fn requires_extra(&self, header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, bool), SubstrateError> {
Ok((header.id(), false))
}
}
+4 -5
View File
@@ -38,10 +38,9 @@ pub type MillauClient = relay_substrate_client::Client<Millau>;
pub type RialtoClient = relay_substrate_client::Client<Rialto>;
mod cli;
mod finality_pipeline;
mod finality_target;
mod headers_initialize;
mod headers_maintain;
mod headers_pipeline;
mod headers_target;
mod messages_lane;
mod messages_source;
mod messages_target;
@@ -101,7 +100,7 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
&rialto_sign.signer,
rialto_signer_next_index,
rialto_runtime::SudoCall::sudo(Box::new(
rialto_runtime::BridgeMillauCall::initialize(initialization_data).into(),
rialto_runtime::FinalityBridgeMillauCall::initialize(initialization_data).into(),
))
.into(),
)
@@ -137,7 +136,7 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
&millau_sign.signer,
millau_signer_next_index,
millau_runtime::SudoCall::sudo(Box::new(
millau_runtime::BridgeRialtoCall::initialize(initialization_data).into(),
millau_runtime::FinalityBridgeRialtoCall::initialize(initialization_data).into(),
))
.into(),
)
@@ -1,4 +1,4 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// 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
@@ -17,70 +17,52 @@
//! Millau-to-Rialto headers sync entrypoint.
use crate::{
headers_pipeline::{SubstrateHeadersSyncPipeline, SubstrateHeadersToSubstrate},
finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate},
MillauClient, RialtoClient,
};
use async_trait::async_trait;
use bp_millau::{
BEST_MILLAU_BLOCKS_METHOD, FINALIZED_MILLAU_BLOCK_METHOD, INCOMPLETE_MILLAU_HEADERS_METHOD,
IS_KNOWN_MILLAU_BLOCK_METHOD,
};
use headers_relay::sync_types::QueuedHeader;
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SyncHeader as MillauSyncHeader};
use relay_rialto_client::{BridgeMillauCall, Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{Error as SubstrateError, TransactionSignScheme};
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{finality_source::Justification, Error as SubstrateError, TransactionSignScheme};
use sp_core::Pair;
use sp_runtime::Justification;
/// Millau-to-Rialto headers sync pipeline.
pub(crate) type MillauHeadersToRialto =
SubstrateHeadersToSubstrate<Millau, MillauSyncHeader, Rialto, RialtoSigningParams>;
/// Millau header in-the-queue.
type QueuedMillauHeader = QueuedHeader<MillauHeadersToRialto>;
/// Millau-to-Rialto finality sync pipeline.
pub(crate) type MillauFinalityToRialto = SubstrateFinalityToSubstrate<Millau, Rialto, RialtoSigningParams>;
#[async_trait]
impl SubstrateHeadersSyncPipeline for MillauHeadersToRialto {
const BEST_BLOCK_METHOD: &'static str = BEST_MILLAU_BLOCKS_METHOD;
const FINALIZED_BLOCK_METHOD: &'static str = FINALIZED_MILLAU_BLOCK_METHOD;
const IS_KNOWN_BLOCK_METHOD: &'static str = IS_KNOWN_MILLAU_BLOCK_METHOD;
const INCOMPLETE_HEADERS_METHOD: &'static str = INCOMPLETE_MILLAU_HEADERS_METHOD;
impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
type SignedTransaction = <Rialto as TransactionSignScheme>::SignedTransaction;
async fn make_submit_header_transaction(
async fn make_submit_finality_proof_transaction(
&self,
header: QueuedMillauHeader,
header: MillauSyncHeader,
proof: Justification<bp_millau::Header>,
) -> Result<Self::SignedTransaction, SubstrateError> {
let account_id = self.target_sign.signer.public().as_array_ref().clone().into();
let nonce = self.target_client.next_account_index(account_id).await?;
let call = BridgeMillauCall::import_signed_header(header.header().clone().into_inner()).into();
let transaction = Rialto::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
Ok(transaction)
}
async fn make_complete_header_transaction(
&self,
id: MillauHeaderId,
completion: Justification,
) -> Result<Self::SignedTransaction, SubstrateError> {
let account_id = self.target_sign.signer.public().as_array_ref().clone().into();
let nonce = self.target_client.next_account_index(account_id).await?;
let call = BridgeMillauCall::finalize_header(id.1, completion).into();
let call = rialto_runtime::FinalityBridgeMillauCall::submit_finality_proof(
header.into_inner(),
proof.into_inner(),
(),
)
.into();
let transaction = Rialto::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
Ok(transaction)
}
}
/// Run Millau-to-Rialto headers sync.
/// Run Millau-to-Rialto finality sync.
pub async fn run(
millau_client: MillauClient,
rialto_client: RialtoClient,
rialto_sign: RialtoSigningParams,
metrics_params: Option<relay_utils::metrics::MetricsParams>,
) {
crate::headers_pipeline::run(
MillauHeadersToRialto::new(rialto_client.clone(), rialto_sign),
crate::finality_pipeline::run(
MillauFinalityToRialto::new(rialto_client.clone(), rialto_sign),
millau_client,
rialto_client,
metrics_params,
@@ -51,8 +51,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
bp_millau::FROM_MILLAU_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_millau::FROM_MILLAU_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::FINALIZED_MILLAU_BLOCK_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rialto::FINALIZED_RIALTO_BLOCK_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
type SourceSignedTransaction = <Millau as TransactionSignScheme>::SignedTransaction;
type TargetSignedTransaction = <Rialto as TransactionSignScheme>::SignedTransaction;
@@ -17,69 +17,52 @@
//! Rialto-to-Millau headers sync entrypoint.
use crate::{
headers_pipeline::{SubstrateHeadersSyncPipeline, SubstrateHeadersToSubstrate},
finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate},
MillauClient, RialtoClient,
};
use async_trait::async_trait;
use bp_rialto::{
BEST_RIALTO_BLOCKS_METHOD, FINALIZED_RIALTO_BLOCK_METHOD, INCOMPLETE_RIALTO_HEADERS_METHOD,
IS_KNOWN_RIALTO_BLOCK_METHOD,
};
use headers_relay::sync_types::QueuedHeader;
use relay_millau_client::{BridgeRialtoCall, Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SyncHeader as RialtoSyncHeader};
use relay_substrate_client::{Error as SubstrateError, TransactionSignScheme};
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader};
use relay_substrate_client::{finality_source::Justification, Error as SubstrateError, TransactionSignScheme};
use sp_core::Pair;
use sp_runtime::Justification;
/// Rialto-to-Millau headers sync pipeline.
type RialtoHeadersToMillau = SubstrateHeadersToSubstrate<Rialto, RialtoSyncHeader, Millau, MillauSigningParams>;
/// Rialto header in-the-queue.
type QueuedRialtoHeader = QueuedHeader<RialtoHeadersToMillau>;
/// Rialto-to-Millau finality sync pipeline.
pub(crate) type RialtoFinalityToMillau = SubstrateFinalityToSubstrate<Rialto, Millau, MillauSigningParams>;
#[async_trait]
impl SubstrateHeadersSyncPipeline for RialtoHeadersToMillau {
const BEST_BLOCK_METHOD: &'static str = BEST_RIALTO_BLOCKS_METHOD;
const FINALIZED_BLOCK_METHOD: &'static str = FINALIZED_RIALTO_BLOCK_METHOD;
const IS_KNOWN_BLOCK_METHOD: &'static str = IS_KNOWN_RIALTO_BLOCK_METHOD;
const INCOMPLETE_HEADERS_METHOD: &'static str = INCOMPLETE_RIALTO_HEADERS_METHOD;
impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
type SignedTransaction = <Millau as TransactionSignScheme>::SignedTransaction;
async fn make_submit_header_transaction(
async fn make_submit_finality_proof_transaction(
&self,
header: QueuedRialtoHeader,
header: RialtoSyncHeader,
proof: Justification<bp_rialto::Header>,
) -> Result<Self::SignedTransaction, SubstrateError> {
let account_id = self.target_sign.signer.public().as_array_ref().clone().into();
let nonce = self.target_client.next_account_index(account_id).await?;
let call = BridgeRialtoCall::import_signed_header(header.header().clone().into_inner()).into();
let transaction = Millau::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
Ok(transaction)
}
async fn make_complete_header_transaction(
&self,
id: RialtoHeaderId,
completion: Justification,
) -> Result<Self::SignedTransaction, SubstrateError> {
let account_id = self.target_sign.signer.public().as_array_ref().clone().into();
let nonce = self.target_client.next_account_index(account_id).await?;
let call = BridgeRialtoCall::finalize_header(id.1, completion).into();
let call = millau_runtime::FinalityBridgeRialtoCall::submit_finality_proof(
header.into_inner(),
proof.into_inner(),
(),
)
.into();
let transaction = Millau::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
Ok(transaction)
}
}
/// Run Rialto-to-Millau headers sync.
/// Run Rialto-to-Millau finality sync.
pub async fn run(
rialto_client: RialtoClient,
millau_client: MillauClient,
millau_sign: MillauSigningParams,
metrics_params: Option<relay_utils::metrics::MetricsParams>,
) {
crate::headers_pipeline::run(
RialtoHeadersToMillau::new(millau_client.clone(), millau_sign),
crate::finality_pipeline::run(
RialtoFinalityToMillau::new(millau_client.clone(), millau_sign),
rialto_client,
millau_client,
metrics_params,
@@ -51,8 +51,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
bp_rialto::FROM_RIALTO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_rialto::FROM_RIALTO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::FINALIZED_RIALTO_BLOCK_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_millau::FINALIZED_MILLAU_BLOCK_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
type SourceSignedTransaction = <Rialto as TransactionSignScheme>::SignedTransaction;
type TargetSignedTransaction = <Millau as TransactionSignScheme>::SignedTransaction;