Sync Westend to Millau (#824)

* make finality verifier pallet instantiable

* add second instance of finality verifier pallet to the Millau runtime

* add Westend -> Millau headers relay

* use wss to connect to public westend nodes

* initialize with best_finalized_block

* typo

* Revert "initialize with best_finalized_block"

This reverts commit 954ed2832372d67618abc1a06d47e66faa93f674.

* pass VoterSet by ref

* new bridge initialization code

* loop upper bound

* Polkadot -> Westend

* fixed tests compilation

* default-features

* assert
This commit is contained in:
Svyatoslav Nikolsky
2021-03-16 10:09:02 +03:00
committed by Bastian Köcher
parent 249a8f73ff
commit d749bc3a96
18 changed files with 474 additions and 136 deletions
+2
View File
@@ -19,6 +19,7 @@ bp-message-lane = { path = "../../../primitives/message-lane", default-features
bp-millau = { path = "../../../primitives/chains/millau", default-features = false }
bp-rialto = { path = "../../../primitives/chains/rialto", default-features = false }
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
bp-westend = { path = "../../../primitives/chains/westend", default-features = false }
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
pallet-bridge-call-dispatch = { path = "../../../modules/call-dispatch", default-features = false }
pallet-finality-verifier = { path = "../../../modules/finality-verifier", default-features = false }
@@ -66,6 +67,7 @@ std = [
"bp-millau/std",
"bp-rialto/std",
"bp-runtime/std",
"bp-westend/std",
"bridge-runtime-common/std",
"codec/std",
"frame-executive/std",
+23 -3
View File
@@ -63,6 +63,7 @@ pub use frame_support::{
pub use frame_system::Call as SystemCall;
pub use pallet_balances::Call as BalancesCall;
pub use pallet_finality_verifier::Call as FinalityBridgeRialtoCall;
pub use pallet_finality_verifier::Call as FinalityBridgeWestendCall;
pub use pallet_message_lane::Call as MessageLaneCall;
pub use pallet_substrate_bridge::Call as BridgeRialtoCall;
pub use pallet_sudo::Call as SudoCall;
@@ -312,11 +313,18 @@ parameter_types! {
pub const MaxRequests: u32 = 50;
}
pub type RialtoFinalityVerifierInstance = ();
impl pallet_finality_verifier::Config for Runtime {
type BridgedChain = bp_rialto::Rialto;
type MaxRequests = MaxRequests;
}
pub type WestendFinalityVerifierInstance = pallet_finality_verifier::Instance1;
impl pallet_finality_verifier::Config<WestendFinalityVerifierInstance> for Runtime {
type BridgedChain = bp_westend::Westend;
type MaxRequests = MaxRequests;
}
impl pallet_shift_session_manager::Config for Runtime {}
parameter_types! {
@@ -371,7 +379,8 @@ construct_runtime!(
BridgeRialto: pallet_substrate_bridge::{Module, Call, Storage, Config<T>},
BridgeRialtoMessageLane: pallet_message_lane::{Module, Call, Storage, Event<T>},
BridgeCallDispatch: pallet_bridge_call_dispatch::{Module, Event<T>},
BridgeFinalityVerifier: pallet_finality_verifier::{Module, Call},
BridgeRialtoFinalityVerifier: pallet_finality_verifier::{Module, Call},
BridgeWestendFinalityVerifier: pallet_finality_verifier::<Instance1>::{Module, Call},
System: frame_system::{Module, Call, Config, Storage, Event<T>},
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},
@@ -572,12 +581,23 @@ impl_runtime_apis! {
impl bp_rialto::RialtoFinalityApi<Block> for Runtime {
fn best_finalized() -> (bp_rialto::BlockNumber, bp_rialto::Hash) {
let header = BridgeFinalityVerifier::best_finalized();
let header = BridgeRialtoFinalityVerifier::best_finalized();
(header.number, header.hash())
}
fn is_known_header(hash: bp_rialto::Hash) -> bool {
BridgeFinalityVerifier::is_known_header(hash)
BridgeRialtoFinalityVerifier::is_known_header(hash)
}
}
impl bp_westend::WestendFinalityApi<Block> for Runtime {
fn best_finalized() -> (bp_westend::BlockNumber, bp_westend::Hash) {
let header = BridgeWestendFinalityVerifier::best_finalized();
(header.number, header.hash())
}
fn is_known_header(hash: bp_westend::Hash) -> bool {
BridgeWestendFinalityVerifier::is_known_header(hash)
}
}
+1 -1
View File
@@ -366,7 +366,7 @@ pub mod pallet {
let set_id = authority_set.set_id;
Ok(
verify_justification::<BridgedHeader<T, I>>((hash, number), set_id, voter_set, &justification).map_err(
verify_justification::<BridgedHeader<T, I>>((hash, number), set_id, &voter_set, &justification).map_err(
|e| {
log::error!("Received invalid justification for {:?}: {:?}", hash, e);
<Error<T, I>>::InvalidJustification
+1 -1
View File
@@ -236,7 +236,7 @@ where
verify_justification::<H>(
(hash, *header.number()),
current_authority_set.set_id,
voter_set,
&voter_set,
&proof.0,
)
.map_err(|_| FinalizationError::InvalidJustification)?;
+1 -1
View File
@@ -20,7 +20,7 @@
// Runtime-generated DecodeLimit::decode_all_with_depth_limit
#![allow(clippy::unnecessary_mut_passed)]
use bp_message_lane::{LaneId, Weight, MessageNonce, UnrewardedRelayersState};
use bp_message_lane::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
use sp_std::prelude::*;
pub use bp_polkadot_core::*;
@@ -57,7 +57,7 @@ pub fn decode_justification_target<Header: HeaderT>(
pub fn verify_justification<Header: HeaderT>(
finalized_target: (Header::Hash, Header::Number),
authorities_set_id: SetId,
authorities_set: VoterSet<AuthorityId>,
authorities_set: &VoterSet<AuthorityId>,
raw_justification: &[u8],
) -> Result<(), Error>
where
@@ -76,7 +76,7 @@ where
// signatures are valid. We'll check the validity of the signatures later since they're more
// resource intensive to verify.
let ancestry_chain = AncestryChain::new(&justification.votes_ancestries);
match finality_grandpa::validate_commit(&justification.commit, &authorities_set, &ancestry_chain) {
match finality_grandpa::validate_commit(&justification.commit, authorities_set, &ancestry_chain) {
Ok(ref result) if result.ghost().is_some() => {}
_ => return Err(Error::InvalidJustificationCommit),
}
@@ -34,7 +34,7 @@ fn make_justification_for_header_1() -> GrandpaJustification<TestHeader> {
#[test]
fn justification_with_invalid_encoding_rejected() {
assert_eq!(
verify_justification::<TestHeader>(header_id::<TestHeader>(1), TEST_GRANDPA_SET_ID, voter_set(), &[],),
verify_justification::<TestHeader>(header_id::<TestHeader>(1), TEST_GRANDPA_SET_ID, &voter_set(), &[],),
Err(Error::JustificationDecode),
);
}
@@ -45,7 +45,7 @@ fn justification_with_invalid_target_rejected() {
verify_justification::<TestHeader>(
header_id::<TestHeader>(2),
TEST_GRANDPA_SET_ID,
voter_set(),
&voter_set(),
&make_justification_for_header_1().encode(),
),
Err(Error::InvalidJustificationTarget),
@@ -61,7 +61,7 @@ fn justification_with_invalid_commit_rejected() {
verify_justification::<TestHeader>(
header_id::<TestHeader>(1),
TEST_GRANDPA_SET_ID,
voter_set(),
&voter_set(),
&justification.encode(),
),
Err(Error::InvalidJustificationCommit),
@@ -77,7 +77,7 @@ fn justification_with_invalid_authority_signature_rejected() {
verify_justification::<TestHeader>(
header_id::<TestHeader>(1),
TEST_GRANDPA_SET_ID,
voter_set(),
&voter_set(),
&justification.encode(),
),
Err(Error::InvalidAuthoritySignature),
@@ -93,7 +93,7 @@ fn justification_with_invalid_precommit_ancestry() {
verify_justification::<TestHeader>(
header_id::<TestHeader>(1),
TEST_GRANDPA_SET_ID,
voter_set(),
&voter_set(),
&justification.encode(),
),
Err(Error::InvalidPrecommitAncestries),
@@ -106,7 +106,7 @@ fn valid_justification_accepted() {
verify_justification::<TestHeader>(
header_id::<TestHeader>(1),
TEST_GRANDPA_SET_ID,
voter_set(),
&voter_set(),
&make_justification_for_header_1().encode(),
),
Ok(()),
@@ -95,7 +95,12 @@ impl<C: Chain> Client<C> {
/// Build client to use in connection.
async fn build_client(params: ConnectionParams) -> Result<RpcClient> {
let uri = format!("ws://{}:{}", params.host, params.port);
let uri = format!(
"{}://{}:{}",
if params.secure { "wss" } else { "ws" },
params.host,
params.port,
);
let mut config = RpcConfig::with_url(&uri);
config.max_subscription_capacity = MAX_SUBSCRIPTION_CAPACITY;
let client = RpcClient::new(config).await?;
@@ -44,6 +44,8 @@ pub struct ConnectionParams {
pub host: String,
/// Websocket server TCP port.
pub port: u16,
/// Use secure websocket connection.
pub secure: bool,
}
impl Default for ConnectionParams {
@@ -51,6 +53,7 @@ impl Default for ConnectionParams {
ConnectionParams {
host: "localhost".into(),
port: 9944,
secure: false,
}
}
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "relay-westend-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
relay-substrate-client = { path = "../substrate" }
relay-utils = { path = "../../generic/utils" }
# Bridge dependencies
bp-westend = { path = "../../../primitives/chains/westend" }
# Substrate Dependencies
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
+47
View File
@@ -0,0 +1,47 @@
// 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/>.
//! Types used to connect to the Westend chain.
use relay_substrate_client::{Chain, ChainBase};
use std::time::Duration;
/// Westend header id.
pub type HeaderId = relay_utils::HeaderId<bp_westend::Hash, bp_westend::BlockNumber>;
/// Westend chain definition
#[derive(Debug, Clone, Copy)]
pub struct Westend;
impl ChainBase for Westend {
type BlockNumber = bp_westend::BlockNumber;
type Hash = bp_westend::Hash;
type Hasher = bp_westend::Hasher;
type Header = bp_westend::Header;
}
impl Chain for Westend {
const NAME: &'static str = "Westend";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
type AccountId = bp_westend::AccountId;
type Index = bp_westend::Nonce;
type SignedBlock = bp_westend::SignedBlock;
type Call = ();
}
/// Westend header type used in headers sync.
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_westend::Header>;
+3
View File
@@ -26,7 +26,9 @@ bp-millau = { path = "../../primitives/chains/millau" }
bp-polkadot = { path = "../../primitives/chains/polkadot" }
bp-runtime = { path = "../../primitives/runtime" }
bp-rialto = { path = "../../primitives/chains/rialto" }
bp-westend = { path = "../../primitives/chains/westend" }
bridge-runtime-common = { path = "../../bin/runtime-common" }
finality-grandpa = { version = "0.14.0" }
finality-relay = { path = "../generic/finality" }
headers-relay = { path = "../generic/headers" }
messages-relay = { path = "../generic/messages" }
@@ -39,6 +41,7 @@ relay-millau-client = { path = "../clients/millau" }
relay-polkadot-client = { path = "../clients/polkadot" }
relay-rialto-client = { path = "../clients/rialto" }
relay-substrate-client = { path = "../clients/substrate" }
relay-westend-client = { path = "../clients/westend" }
relay-utils = { path = "../generic/utils" }
rialto-runtime = { path = "../../bin/rialto/runtime" }
+3 -14
View File
@@ -396,6 +396,9 @@ macro_rules! declare_chain_options {
#[doc = "Connect to " $chain " node websocket server at given port."]
#[structopt(long)]
pub [<$chain_prefix _port>]: u16,
#[doc = "Use secure websocket connection."]
#[structopt(long)]
pub [<$chain_prefix _secure>]: bool,
}
#[doc = $chain " signing params."]
@@ -408,20 +411,6 @@ macro_rules! declare_chain_options {
#[structopt(long)]
pub [<$chain_prefix _signer_password>]: Option<String>,
}
#[doc = $chain " headers bridge initialization params."]
#[derive(StructOpt)]
pub struct [<$chain BridgeInitializationParams>] {
#[doc = "Hex-encoded " $chain " header to initialize bridge with. If not specified, genesis header is used."]
#[structopt(long)]
pub [<$chain_prefix _initial_header>]: Option<sp_core::Bytes>,
#[doc = "Hex-encoded " $chain " GRANDPA authorities set to initialize bridge with. If not specified, set from genesis block is used."]
#[structopt(long)]
pub [<$chain_prefix _initial_authorities>]: Option<sp_core::Bytes>,
#[doc = "Id of the " $chain " GRANDPA authorities set to initialize bridge with. If not specified, zero is used."]
#[structopt(long)]
pub [<$chain_prefix _initial_authorities_set_id>]: Option<sp_finality_grandpa::SetId>,
}
}
};
}
@@ -21,30 +21,26 @@
//! and authorities set from source to target chain. The headers sync starts
//! with this header.
use bp_header_chain::{
find_grandpa_authorities_scheduled_change,
justification::{decode_justification_target, verify_justification},
};
use codec::Decode;
use finality_grandpa::voter_set::VoterSet;
use num_traits::{One, Zero};
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};
use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet;
use sp_runtime::traits::Header as HeaderT;
/// Submit headers-bridge initialization transaction.
pub async fn initialize<SourceChain: Chain, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
raw_initial_header: Option<Bytes>,
raw_initial_authorities_set: Option<Bytes>,
initial_authorities_set_id: Option<GrandpaAuthoritiesSetId>,
prepare_initialize_transaction: impl FnOnce(InitializationData<SourceChain::Header>) -> Result<Bytes, String>,
) {
let result = do_initialize(
source_client,
target_client,
raw_initial_header,
raw_initial_authorities_set,
initial_authorities_set_id,
prepare_initialize_transaction,
)
.await;
let result = do_initialize(source_client, target_client, prepare_initialize_transaction).await;
match result {
Ok(tx_hash) => log::info!(
@@ -68,18 +64,17 @@ pub async fn initialize<SourceChain: Chain, TargetChain: Chain>(
async fn do_initialize<SourceChain: Chain, TargetChain: Chain>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
raw_initial_header: Option<Bytes>,
raw_initial_authorities_set: Option<Bytes>,
initial_authorities_set_id: Option<GrandpaAuthoritiesSetId>,
prepare_initialize_transaction: impl FnOnce(InitializationData<SourceChain::Header>) -> Result<Bytes, String>,
) -> Result<TargetChain::Hash, String> {
let initialization_data = prepare_initialization_data(
source_client,
raw_initial_header,
raw_initial_authorities_set,
initial_authorities_set_id,
)
.await?;
let initialization_data = prepare_initialization_data(source_client).await?;
log::info!(
target: "bridge",
"Prepared initialization data for {}-headers bridge at {}: {:?}",
SourceChain::NAME,
TargetChain::NAME,
initialization_data,
);
let initialization_tx = prepare_initialize_transaction(initialization_data)?;
let initialization_tx_hash = target_client
.submit_extrinsic(initialization_tx)
@@ -88,50 +83,161 @@ async fn do_initialize<SourceChain: Chain, TargetChain: Chain>(
Ok(initialization_tx_hash)
}
/// Prepare initialization data for the headers-bridge pallet.
/// Prepare initialization data for the finality-verifier pallet.
async fn prepare_initialization_data<SourceChain: Chain>(
source_client: Client<SourceChain>,
raw_initial_header: Option<Bytes>,
raw_initial_authorities_set: Option<Bytes>,
initial_authorities_set_id: Option<GrandpaAuthoritiesSetId>,
) -> Result<InitializationData<SourceChain::Header>, String> {
let source_genesis_hash = *source_client.genesis_hash();
// In ideal world we just need to get best finalized header and then to read GRANDPA authorities
// set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at this header.
//
// But now there are problems with this approach - `CurrentSetId` may return invalid value. So here
// we're waiting for the next justification, read the authorities set and then try to figure out
// the set id with bruteforce.
let mut justifications = source_client
.subscribe_justifications()
.await
.map_err(|err| format!("Failed to subscribe to {} justifications: {:?}", SourceChain::NAME, err))?;
let initial_header = match raw_initial_header {
Some(raw_initial_header) => SourceChain::Header::decode(&mut &raw_initial_header.0[..])
.map_err(|err| format!("Failed to decode {} initial header: {:?}", SourceChain::NAME, err))?,
None => source_client
.header_by_hash(source_genesis_hash)
.await
.map_err(|err| format!("Failed to retrive {} genesis header: {:?}", SourceChain::NAME, err))?,
};
// Read next justification - the header that it finalizes will be used as initial header.
let justification = justifications.next().await.ok_or_else(|| {
format!(
"Failed to read {} justification from the stream: stream has ended unexpectedly",
SourceChain::NAME,
)
})?;
let raw_initial_authorities_set = match raw_initial_authorities_set {
Some(raw_initial_authorities_set) => raw_initial_authorities_set.0,
None => source_client
.grandpa_authorities_set(source_genesis_hash)
.await
.map_err(|err| {
format!(
"Failed to retrive {} authorities set at genesis header: {:?}",
SourceChain::NAME,
err
)
})?,
};
let initial_authorities_set =
GrandpaAuthoritiesSet::decode(&mut &raw_initial_authorities_set[..]).map_err(|err| {
format!(
"Failed to decode {} initial authorities set: {:?}",
// Read initial header.
let (initial_header_hash, initial_header_number) =
decode_justification_target::<SourceChain::Header>(&justification.0)
.map_err(|err| format!("Failed to decode {} justification: {:?}", SourceChain::NAME, err))?;
let initial_header = source_header(&source_client, initial_header_hash).await?;
log::trace!(target: "bridge", "Selected {} initial header: {}/{}",
SourceChain::NAME,
initial_header_number,
initial_header_hash,
);
// Read GRANDPA authorities set at initial header.
let initial_authorities_set = source_authorities_set(&source_client, initial_header_hash).await?;
log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}",
SourceChain::NAME,
initial_authorities_set,
);
// If initial header changes the GRANDPA authorities set, then we need previous authorities
// to verify justification.
let mut authorities_for_verification = initial_authorities_set.clone();
let scheduled_change = find_grandpa_authorities_scheduled_change(&initial_header);
assert!(
scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true),
"GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\
regular hange to have zero delay",
initial_header_hash,
scheduled_change.as_ref().map(|c| c.delay),
);
let schedules_change = scheduled_change.is_some();
if schedules_change {
authorities_for_verification = source_authorities_set(&source_client, *initial_header.parent_hash()).await?;
log::trace!(
target: "bridge",
"Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}",
SourceChain::NAME,
authorities_for_verification,
);
}
// Now let's try to guess authorities set id by verifying justification.
let mut initial_authorities_set_id = 0;
let mut min_possible_block_number = SourceChain::BlockNumber::zero();
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()).ok_or_else(|| {
format!(
"Read invalid {} authorities set: {:?}",
SourceChain::NAME,
authorities_for_verification,
)
})?;
loop {
log::trace!(
target: "bridge", "Trying {} GRANDPA authorities set id: {}",
SourceChain::NAME,
initial_authorities_set_id,
);
let is_valid_set_id = verify_justification::<SourceChain::Header>(
(initial_header_hash, initial_header_number),
initial_authorities_set_id,
&authorities_for_verification,
&justification.0,
)
.is_ok();
if is_valid_set_id {
break;
}
initial_authorities_set_id += 1;
min_possible_block_number += One::one();
if min_possible_block_number > initial_header_number {
// there can't be more authorities set changes than headers => if we have reached `initial_block_number`
// and still have not found correct value of `initial_authorities_set_id`, then something
// else is broken => fail
return Err(format!(
"Failed to guess initial {} GRANDPA authorities set id: checked all\
possible ids in range [0; {}]",
SourceChain::NAME,
err
)
})?;
initial_header_number
));
}
}
Ok(InitializationData {
header: initial_header,
authority_list: initial_authorities_set,
set_id: initial_authorities_set_id.unwrap_or(0),
set_id: if schedules_change {
initial_authorities_set_id + 1
} else {
initial_authorities_set_id
},
is_halted: false,
})
}
/// Read header by hash from the source client.
async fn source_header<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<SourceChain::Header, String> {
source_client.header_by_hash(header_hash).await.map_err(|err| {
format!(
"Failed to retrive {} header with hash {}: {:?}",
SourceChain::NAME,
header_hash,
err,
)
})
}
/// Read GRANDPA authorities set at given header.
async fn source_authorities_set<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<GrandpaAuthoritiesSet, String> {
let raw_authorities_set = source_client
.grandpa_authorities_set(header_hash)
.await
.map_err(|err| {
format!(
"Failed to retrive {} GRANDPA authorities set at header {}: {:?}",
SourceChain::NAME,
header_hash,
err,
)
})?;
GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]).map_err(|err| {
format!(
"Failed to decode {} GRANDPA authorities set at header {}: {:?}",
SourceChain::NAME,
header_hash,
err,
)
})
}
@@ -47,6 +47,17 @@ pub enum RelayHeaders {
#[structopt(flatten)]
prometheus_params: PrometheusParams,
},
/// Relay Westend headers to Millau.
WestendToMillau {
#[structopt(flatten)]
westend: WestendConnectionParams,
#[structopt(flatten)]
millau: MillauConnectionParams,
#[structopt(flatten)]
millau_sign: MillauSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
},
}
impl RelayHeaders {
@@ -113,8 +124,6 @@ pub enum InitBridge {
rialto: RialtoConnectionParams,
#[structopt(flatten)]
rialto_sign: RialtoSigningParams,
#[structopt(flatten)]
millau_bridge_params: MillauBridgeInitializationParams,
},
/// Initialize Rialto headers bridge in Millau.
RialtoToMillau {
@@ -124,8 +133,15 @@ pub enum InitBridge {
millau: MillauConnectionParams,
#[structopt(flatten)]
millau_sign: MillauSigningParams,
},
/// Initialize Westend headers bridge in Millau.
WestendToMillau {
#[structopt(flatten)]
rialto_bridge_params: RialtoBridgeInitializationParams,
westend: WestendConnectionParams,
#[structopt(flatten)]
millau: MillauConnectionParams,
#[structopt(flatten)]
millau_sign: MillauSigningParams,
},
}
@@ -421,3 +437,4 @@ pub enum ToMillauMessage {
declare_chain_options!(Rialto, rialto);
declare_chain_options!(Millau, millau);
declare_chain_options!(Westend, westend);
@@ -21,11 +21,14 @@ pub mod millau_headers_to_rialto;
pub mod millau_messages_to_rialto;
pub mod rialto_headers_to_millau;
pub mod rialto_messages_to_millau;
pub mod westend_headers_to_millau;
/// Millau node client.
pub type MillauClient = relay_substrate_client::Client<Millau>;
/// Rialto node client.
pub type RialtoClient = relay_substrate_client::Client<Rialto>;
/// Westend node client.
pub type WestendClient = relay_substrate_client::Client<Westend>;
use crate::cli::{ExplicitOrMaximal, HexBytes, Origins};
use codec::{Decode, Encode};
@@ -34,6 +37,7 @@ use pallet_bridge_call_dispatch::{CallOrigin, MessagePayload};
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{Chain, ConnectionParams, TransactionSignScheme};
use relay_westend_client::Westend;
use sp_core::{Bytes, Pair};
use sp_runtime::traits::IdentifyAccount;
use std::fmt::Debug;
@@ -44,7 +48,6 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
millau,
rialto,
rialto_sign,
millau_bridge_params,
} => {
let millau_client = millau.into_client().await?;
let rialto_client = rialto.into_client().await?;
@@ -54,34 +57,26 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
.next_account_index(rialto_sign.signer.public().into())
.await?;
crate::headers_initialize::initialize(
millau_client,
rialto_client.clone(),
millau_bridge_params.millau_initial_header,
millau_bridge_params.millau_initial_authorities,
millau_bridge_params.millau_initial_authorities_set_id,
move |initialization_data| {
Ok(Bytes(
Rialto::sign_transaction(
*rialto_client.genesis_hash(),
&rialto_sign.signer,
rialto_signer_next_index,
rialto_runtime::SudoCall::sudo(Box::new(
rialto_runtime::FinalityBridgeMillauCall::initialize(initialization_data).into(),
))
.into(),
)
.encode(),
))
},
)
crate::headers_initialize::initialize(millau_client, rialto_client.clone(), move |initialization_data| {
Ok(Bytes(
Rialto::sign_transaction(
*rialto_client.genesis_hash(),
&rialto_sign.signer,
rialto_signer_next_index,
rialto_runtime::SudoCall::sudo(Box::new(
rialto_runtime::FinalityBridgeMillauCall::initialize(initialization_data).into(),
))
.into(),
)
.encode(),
))
})
.await;
}
cli::InitBridge::RialtoToMillau {
rialto,
millau,
millau_sign,
rialto_bridge_params,
} => {
let rialto_client = rialto.into_client().await?;
let millau_client = millau.into_client().await?;
@@ -90,27 +85,52 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
.next_account_index(millau_sign.signer.public().into())
.await?;
crate::headers_initialize::initialize(
rialto_client,
millau_client.clone(),
rialto_bridge_params.rialto_initial_header,
rialto_bridge_params.rialto_initial_authorities,
rialto_bridge_params.rialto_initial_authorities_set_id,
move |initialization_data| {
Ok(Bytes(
Millau::sign_transaction(
*millau_client.genesis_hash(),
&millau_sign.signer,
millau_signer_next_index,
millau_runtime::SudoCall::sudo(Box::new(
millau_runtime::FinalityBridgeRialtoCall::initialize(initialization_data).into(),
))
.into(),
)
.encode(),
))
},
)
crate::headers_initialize::initialize(rialto_client, millau_client.clone(), move |initialization_data| {
let initialize_call = millau_runtime::FinalityBridgeRialtoCall::<
millau_runtime::Runtime,
millau_runtime::RialtoFinalityVerifierInstance,
>::initialize(initialization_data);
Ok(Bytes(
Millau::sign_transaction(
*millau_client.genesis_hash(),
&millau_sign.signer,
millau_signer_next_index,
millau_runtime::SudoCall::sudo(Box::new(initialize_call.into())).into(),
)
.encode(),
))
})
.await;
}
cli::InitBridge::WestendToMillau {
westend,
millau,
millau_sign,
} => {
let westend_client = westend.into_client().await?;
let millau_client = millau.into_client().await?;
let millau_sign = millau_sign.parse()?;
let millau_signer_next_index = millau_client
.next_account_index(millau_sign.signer.public().into())
.await?;
crate::headers_initialize::initialize(westend_client, millau_client.clone(), move |initialization_data| {
let initialize_call = millau_runtime::FinalityBridgeWestendCall::<
millau_runtime::Runtime,
millau_runtime::WestendFinalityVerifierInstance,
>::initialize(initialization_data);
Ok(Bytes(
Millau::sign_transaction(
*millau_client.genesis_hash(),
&millau_sign.signer,
millau_signer_next_index,
millau_runtime::SudoCall::sudo(Box::new(initialize_call.into())).into(),
)
.encode(),
))
})
.await;
}
}
@@ -141,6 +161,17 @@ async fn run_relay_headers(command: cli::RelayHeaders) -> Result<(), String> {
let millau_sign = millau_sign.parse()?;
rialto_headers_to_millau::run(rialto_client, millau_client, millau_sign, prometheus_params.into()).await;
}
cli::RelayHeaders::WestendToMillau {
westend,
millau,
millau_sign,
prometheus_params,
} => {
let westend_client = westend.into_client().await?;
let millau_client = millau.into_client().await?;
let millau_sign = millau_sign.parse()?;
westend_headers_to_millau::run(westend_client, millau_client, millau_sign, prometheus_params.into()).await;
}
}
Ok(())
}
@@ -661,16 +692,31 @@ impl cli::MillauConnectionParams {
MillauClient::new(ConnectionParams {
host: self.millau_host,
port: self.millau_port,
secure: self.millau_secure,
})
.await
}
}
impl cli::RialtoConnectionParams {
/// Convert CLI connection parameters into Rialto RPC Client.
pub async fn into_client(self) -> relay_substrate_client::Result<RialtoClient> {
RialtoClient::new(ConnectionParams {
host: self.rialto_host,
port: self.rialto_port,
secure: self.rialto_secure,
})
.await
}
}
impl cli::WestendConnectionParams {
/// Convert CLI connection parameters into Westend RPC Client.
pub async fn into_client(self) -> relay_substrate_client::Result<WestendClient> {
WestendClient::new(ConnectionParams {
host: self.westend_host,
port: self.westend_port,
secure: self.westend_secure,
})
.await
}
@@ -41,9 +41,12 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
) -> 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 =
millau_runtime::FinalityBridgeRialtoCall::submit_finality_proof(header.into_inner(), proof.into_inner())
.into();
let call = millau_runtime::FinalityBridgeRialtoCall::<
millau_runtime::Runtime,
millau_runtime::RialtoFinalityVerifierInstance,
>::submit_finality_proof(header.into_inner(), proof.into_inner())
.into();
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Millau::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
@@ -0,0 +1,72 @@
// 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/>.
//! Westend-to-Millau headers sync entrypoint.
use super::{MillauClient, WestendClient};
use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use async_trait::async_trait;
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_substrate_client::{finality_source::Justification, Error as SubstrateError, TransactionSignScheme};
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
use sp_core::Pair;
/// Westend-to-Millau finality sync pipeline.
pub(crate) type WestendFinalityToMillau = SubstrateFinalityToSubstrate<Westend, Millau, MillauSigningParams>;
#[async_trait]
impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
type SignedTransaction = <Millau as TransactionSignScheme>::SignedTransaction;
async fn make_submit_finality_proof_transaction(
&self,
header: WestendSyncHeader,
proof: Justification<bp_westend::BlockNumber>,
) -> 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 = millau_runtime::FinalityBridgeWestendCall::<
millau_runtime::Runtime,
millau_runtime::WestendFinalityVerifierInstance,
>::submit_finality_proof(header.into_inner(), proof.into_inner())
.into();
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Millau::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
Ok(transaction)
}
}
/// Run Westend-to-Millau finality sync.
pub async fn run(
westend_client: WestendClient,
millau_client: MillauClient,
millau_sign: MillauSigningParams,
metrics_params: Option<relay_utils::metrics::MetricsParams>,
) {
crate::finality_pipeline::run(
WestendFinalityToMillau::new(millau_client.clone(), millau_sign),
westend_client,
millau_client,
metrics_params,
)
.await;
}