Squashed 'bridges/' content from commit 89a76998f

git-subtree-dir: bridges
git-subtree-split: 89a76998f93c8219e9b1f785dcce73d4891e7068
This commit is contained in:
Hernando Castano
2021-04-21 11:56:23 -04:00
commit e5bed7ac38
339 changed files with 71658 additions and 0 deletions
@@ -0,0 +1,96 @@
// 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/>.
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Debug, PartialEq, Eq)]
/// Supported full bridges (headers + messages).
pub enum FullBridge {
MillauToRialto,
RialtoToMillau,
}
}
impl FullBridge {
/// Return instance index of the bridge pallet in source runtime.
pub fn bridge_instance_index(&self) -> u8 {
match self {
Self::MillauToRialto => MILLAU_TO_RIALTO_INDEX,
Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX,
}
}
}
pub const RIALTO_TO_MILLAU_INDEX: u8 = 0;
pub const MILLAU_TO_RIALTO_INDEX: u8 = 0;
/// The macro allows executing bridge-specific code without going fully generic.
///
/// It matches on the [`FullBridge`] enum, sets bridge-specific types or imports and injects
/// the `$generic` code at every variant.
#[macro_export]
macro_rules! select_full_bridge {
($bridge: expr, $generic: tt) => {
match $bridge {
FullBridge::MillauToRialto => {
type Source = relay_millau_client::Millau;
#[allow(dead_code)]
type Target = relay_rialto_client::Rialto;
// Derive-account
#[allow(unused_imports)]
use bp_millau::derive_account_from_rialto_id as derive_account;
// Relay-messages
#[allow(unused_imports)]
use crate::chains::millau_messages_to_rialto::run as relay_messages;
// Send-message / Estimate-fee
#[allow(unused_imports)]
use bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
// Send-message
#[allow(unused_imports)]
use millau_runtime::rialto_account_ownership_digest as account_ownership_digest;
$generic
}
FullBridge::RialtoToMillau => {
type Source = relay_rialto_client::Rialto;
#[allow(dead_code)]
type Target = relay_millau_client::Millau;
// Derive-account
#[allow(unused_imports)]
use bp_rialto::derive_account_from_millau_id as derive_account;
// Relay-messages
#[allow(unused_imports)]
use crate::chains::rialto_messages_to_millau::run as relay_messages;
// Send-message / Estimate-fee
#[allow(unused_imports)]
use bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
// Send-message
#[allow(unused_imports)]
use rialto_runtime::millau_account_ownership_digest as account_ownership_digest;
$generic
}
}
};
}
@@ -0,0 +1,102 @@
// 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/>.
use crate::cli::{bridge::FullBridge, AccountId};
use crate::select_full_bridge;
use relay_substrate_client::Chain;
use structopt::StructOpt;
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
///
/// The (derived) target chain `AccountId` is going to be used as dispatch origin of the call
/// that has been sent over the bridge.
/// This account can also be used to receive target-chain funds (or other form of ownership),
/// since messages sent over the bridge will be able to spend these.
#[derive(StructOpt)]
pub struct DeriveAccount {
/// A bridge instance to initalize.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
/// Source-chain address to derive Target-chain address from.
account: AccountId,
}
impl DeriveAccount {
/// Parse CLI arguments and derive account.
///
/// Returns both the Source account in correct SS58 format and the derived account.
fn derive_account(&self) -> (AccountId, AccountId) {
select_full_bridge!(self.bridge, {
let mut account = self.account.clone();
account.enforce_chain::<Source>();
let acc = bp_runtime::SourceAccount::Account(account.raw_id());
let id = derive_account(acc);
let derived_account = AccountId::from_raw::<Target>(id);
(account, derived_account)
})
}
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
select_full_bridge!(self.bridge, {
let (account, derived_account) = self.derive_account();
println!("Source address:\n{} ({})", account, Source::NAME);
println!(
"->Corresponding (derived) address:\n{} ({})",
derived_account,
Target::NAME,
);
Ok(())
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn derive_account_cli(bridge: &str, account: &str) -> (AccountId, AccountId) {
DeriveAccount::from_iter(vec!["derive-account", bridge, account]).derive_account()
}
#[test]
fn should_derive_accounts_correctly() {
// given
let rialto = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU";
let millau = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9";
// when
let (rialto_parsed, rialto_derived) = derive_account_cli("RialtoToMillau", rialto);
let (millau_parsed, millau_derived) = derive_account_cli("MillauToRialto", millau);
let (millau2_parsed, millau2_derived) = derive_account_cli("MillauToRialto", rialto);
// then
assert_eq!(format!("{}", rialto_parsed), rialto);
assert_eq!(format!("{}", millau_parsed), millau);
assert_eq!(format!("{}", millau2_parsed), millau);
assert_eq!(
format!("{}", rialto_derived),
"73gLnUwrAdH4vMjbXCiNEpgyz1PLk9JxCaY4cKzvfSZT73KE"
);
assert_eq!(
format!("{}", millau_derived),
"5rpTJqGv1BPAYy2sXzkPpc3Wx1ZpQtgfuBsrDpNV4HsXAmbi"
);
assert_eq!(millau_derived, millau2_derived);
}
}
@@ -0,0 +1,275 @@
// 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/>.
use crate::cli::bridge::FullBridge;
use crate::cli::{AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId};
use crate::select_full_bridge;
use frame_support::dispatch::GetDispatchInfo;
use relay_substrate_client::Chain;
use structopt::StructOpt;
/// Encode source chain runtime call.
#[derive(StructOpt, Debug)]
pub struct EncodeCall {
/// A bridge instance to encode call for.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
call: Call,
}
/// All possible messages that may be delivered to generic Substrate chain.
///
/// Note this enum may be used in the context of both Source (as part of `encode-call`)
/// and Target chain (as part of `encode-message/send-message`).
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub enum Call {
/// Raw bytes for the message
Raw {
/// Raw, SCALE-encoded message
data: HexBytes,
},
/// Make an on-chain remark (comment).
Remark {
/// Explicit remark payload.
#[structopt(long, conflicts_with("remark-size"))]
remark_payload: Option<HexBytes>,
/// Remark size. If not passed, small UTF8-encoded string is generated by relay as remark.
#[structopt(long, conflicts_with("remark-payload"))]
remark_size: Option<ExplicitOrMaximal<usize>>,
},
/// Transfer the specified `amount` of native tokens to a particular `recipient`.
Transfer {
/// Address of an account to receive the transfer.
#[structopt(long)]
recipient: AccountId,
/// Amount of target tokens to send in target chain base currency units.
#[structopt(long)]
amount: Balance,
},
/// A call to the specific Bridge Messages pallet to queue message to be sent over a bridge.
BridgeSendMessage {
/// An index of the bridge instance which represents the expected target chain.
#[structopt(skip = 255)]
bridge_instance_index: u8,
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// Raw SCALE-encoded Message Payload to submit to the messages pallet.
///
/// This can be obtained by encoding call for the target chain.
#[structopt(long)]
payload: HexBytes,
/// Declared delivery and dispatch fee in base source-chain currency units.
#[structopt(long)]
fee: Balance,
},
}
pub trait CliEncodeCall: Chain {
/// Maximal size (in bytes) of any extrinsic (from the runtime).
fn max_extrinsic_size() -> u32;
/// Encode a CLI call.
fn encode_call(call: &Call) -> anyhow::Result<Self::Call>;
}
impl EncodeCall {
fn encode(&mut self) -> anyhow::Result<HexBytes> {
select_full_bridge!(self.bridge, {
preprocess_call::<Source, Target>(&mut self.call, self.bridge.bridge_instance_index());
let call = Source::encode_call(&self.call)?;
let encoded = HexBytes::encode(&call);
log::info!(target: "bridge", "Generated {} call: {:#?}", Source::NAME, call);
log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, call.get_dispatch_info().weight);
log::info!(target: "bridge", "Encoded {} call: {:?}", Source::NAME, encoded);
Ok(encoded)
})
}
/// Run the command.
pub async fn run(mut self) -> anyhow::Result<()> {
println!("{:?}", self.encode()?);
Ok(())
}
}
/// Prepare the call to be passed to [`CliEncodeCall::encode_call`].
///
/// This function will fill in all optional and missing pieces and will make sure that
/// values are converted to bridge-specific ones.
///
/// Most importantly, the method will fill-in [`bridge_instance_index`] parameter for
/// target-chain specific calls.
pub(crate) fn preprocess_call<Source: CliEncodeCall + CliChain, Target: CliEncodeCall>(
call: &mut Call,
bridge_instance: u8,
) {
match *call {
Call::Raw { .. } => {}
Call::Remark {
ref remark_size,
ref mut remark_payload,
} => {
if remark_payload.is_none() {
*remark_payload = Some(HexBytes(generate_remark_payload(
&remark_size,
compute_maximal_message_arguments_size(Source::max_extrinsic_size(), Target::max_extrinsic_size()),
)));
}
}
Call::Transfer { ref mut recipient, .. } => {
recipient.enforce_chain::<Source>();
}
Call::BridgeSendMessage {
ref mut bridge_instance_index,
..
} => {
*bridge_instance_index = bridge_instance;
}
};
}
fn generate_remark_payload(remark_size: &Option<ExplicitOrMaximal<usize>>, maximal_allowed_size: u32) -> Vec<u8> {
match remark_size {
Some(ExplicitOrMaximal::Explicit(remark_size)) => vec![0; *remark_size],
Some(ExplicitOrMaximal::Maximal) => vec![0; maximal_allowed_size as _],
None => format!(
"Unix time: {}",
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
)
.as_bytes()
.to_vec(),
}
}
pub(crate) fn compute_maximal_message_arguments_size(
maximal_source_extrinsic_size: u32,
maximal_target_extrinsic_size: u32,
) -> u32 {
// assume that both signed extensions and other arguments fit 1KB
let service_tx_bytes_on_source_chain = 1024;
let maximal_source_extrinsic_size = maximal_source_extrinsic_size - service_tx_bytes_on_source_chain;
let maximal_call_size =
bridge_runtime_common::messages::target::maximal_incoming_message_size(maximal_target_extrinsic_size);
let maximal_call_size = if maximal_call_size > maximal_source_extrinsic_size {
maximal_source_extrinsic_size
} else {
maximal_call_size
};
// bytes in Call encoding that are used to encode everything except arguments
let service_bytes = 1 + 1 + 4;
maximal_call_size - service_bytes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_encode_transfer_call() {
// given
let mut encode_call = EncodeCall::from_iter(vec![
"encode-call",
"RialtoToMillau",
"transfer",
"--amount",
"12345",
"--recipient",
"5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU",
]);
// when
let hex = encode_call.encode().unwrap();
// then
assert_eq!(
format!("{:?}", hex),
"0x0c00d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0"
);
}
#[test]
fn should_encode_remark_with_default_payload() {
// given
let mut encode_call = EncodeCall::from_iter(vec!["encode-call", "RialtoToMillau", "remark"]);
// when
let hex = encode_call.encode().unwrap();
// then
assert!(format!("{:?}", hex).starts_with("0x070154556e69782074696d653a"));
}
#[test]
fn should_encode_remark_with_explicit_payload() {
// given
let mut encode_call = EncodeCall::from_iter(vec![
"encode-call",
"RialtoToMillau",
"remark",
"--remark-payload",
"1234",
]);
// when
let hex = encode_call.encode().unwrap();
// then
assert_eq!(format!("{:?}", hex), "0x0701081234");
}
#[test]
fn should_encode_remark_with_size() {
// given
let mut encode_call =
EncodeCall::from_iter(vec!["encode-call", "RialtoToMillau", "remark", "--remark-size", "12"]);
// when
let hex = encode_call.encode().unwrap();
// then
assert_eq!(format!("{:?}", hex), "0x070130000000000000000000000000");
}
#[test]
fn should_disallow_both_payload_and_size() {
// when
let err = EncodeCall::from_iter_safe(vec![
"encode-call",
"RialtoToMillau",
"remark",
"--remark-payload",
"1234",
"--remark-size",
"12",
])
.unwrap_err();
// then
assert_eq!(err.kind, structopt::clap::ErrorKind::ArgumentConflict);
let info = err.info.unwrap();
assert!(info.contains(&"remark-payload".to_string()) | info.contains(&"remark-size".to_string()))
}
}
@@ -0,0 +1,106 @@
// 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/>.
use crate::cli::{bridge::FullBridge, AccountId, CliChain, HexBytes};
use crate::select_full_bridge;
use structopt::StructOpt;
/// Generic message payload.
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub enum MessagePayload {
/// Raw, SCALE-encoded `MessagePayload`.
Raw {
/// Hex-encoded SCALE data.
data: HexBytes,
},
/// Construct message to send over the bridge.
Call {
/// Message details.
#[structopt(flatten)]
call: crate::cli::encode_call::Call,
/// SS58 encoded Source account that will send the payload.
#[structopt(long)]
sender: AccountId,
},
}
/// A `MessagePayload` to encode.
#[derive(StructOpt)]
pub struct EncodeMessage {
/// A bridge instance to initalize.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
payload: MessagePayload,
}
impl EncodeMessage {
/// Run the command.
pub fn encode(self) -> anyhow::Result<HexBytes> {
select_full_bridge!(self.bridge, {
let payload = Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?;
Ok(HexBytes::encode(&payload))
})
}
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
let payload = self.encode()?;
println!("{:?}", payload);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use sp_core::crypto::Ss58Codec;
#[test]
fn should_encode_raw_message() {
// given
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000";
let encode_message = EncodeMessage::from_iter(vec!["encode-message", "MillauToRialto", "raw", msg]);
// when
let hex = encode_message.encode().unwrap();
// then
assert_eq!(format!("{:?}", hex), format!("0x{}", msg));
}
#[test]
fn should_encode_remark_with_size() {
// given
let sender = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check();
let encode_message = EncodeMessage::from_iter(vec![
"encode-message",
"RialtoToMillau",
"call",
"--sender",
&sender,
"remark",
"--remark-size",
"12",
]);
// when
let hex = encode_message.encode().unwrap();
// then
assert_eq!(format!("{:?}", hex), "0x01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000");
}
}
@@ -0,0 +1,128 @@
// 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/>.
use crate::cli::bridge::FullBridge;
use crate::cli::{Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams};
use crate::select_full_bridge;
use codec::{Decode, Encode};
use relay_substrate_client::{Chain, ChainWithBalances};
use structopt::StructOpt;
/// Estimate Delivery & Dispatch Fee command.
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub struct EstimateFee {
/// A bridge instance to encode call for.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
source: SourceConnectionParams,
/// Hex-encoded id of lane that will be delivering the message.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// Payload to send over the bridge.
#[structopt(flatten)]
payload: crate::cli::encode_message::MessagePayload,
}
impl EstimateFee {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
let Self {
source,
bridge,
lane,
payload,
} = self;
select_full_bridge!(bridge, {
let source_client = source.to_client::<Source>().await?;
let lane = lane.into();
let payload = Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
let fee: <Source as ChainWithBalances>::NativeBalance =
estimate_message_delivery_and_dispatch_fee(&source_client, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload)
.await?;
log::info!(target: "bridge", "Fee: {:?}", Balance(fee as _));
println!("{}", fee);
Ok(())
})
}
}
pub(crate) async fn estimate_message_delivery_and_dispatch_fee<Fee: Decode, C: Chain, P: Encode>(
client: &relay_substrate_client::Client<C>,
estimate_fee_method: &str,
lane: bp_messages::LaneId,
payload: P,
) -> anyhow::Result<Fee> {
let encoded_response = client
.state_call(estimate_fee_method.into(), (lane, payload).encode().into(), None)
.await?;
let decoded_response: Option<Fee> =
Decode::decode(&mut &encoded_response.0[..]).map_err(relay_substrate_client::Error::ResponseParseFailed)?;
let fee = decoded_response
.ok_or_else(|| anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec())))?;
Ok(fee)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::encode_call;
use sp_core::crypto::Ss58Codec;
#[test]
fn should_parse_cli_options() {
// given
let alice = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check();
// when
let res = EstimateFee::from_iter(vec![
"estimate_fee",
"RialtoToMillau",
"--source-port",
"1234",
"call",
"--sender",
&alice,
"remark",
"--remark-payload",
"1234",
]);
// then
assert_eq!(
res,
EstimateFee {
bridge: FullBridge::RialtoToMillau,
lane: HexLaneId([0, 0, 0, 0]),
source: SourceConnectionParams {
source_host: "127.0.0.1".into(),
source_port: 1234,
source_secure: false,
},
payload: crate::cli::encode_message::MessagePayload::Call {
sender: alice.parse().unwrap(),
call: encode_call::Call::Remark {
remark_payload: Some(HexBytes(vec![0x12, 0x34])),
remark_size: None,
}
}
}
);
}
}
@@ -0,0 +1,162 @@
// 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/>.
use crate::cli::{SourceConnectionParams, TargetConnectionParams, TargetSigningParams};
use bp_header_chain::InitializationData;
use bp_runtime::Chain as ChainBase;
use codec::Encode;
use relay_substrate_client::{Chain, TransactionSignScheme};
use sp_core::{Bytes, Pair};
use structopt::{clap::arg_enum, StructOpt};
/// Initialize bridge pallet.
#[derive(StructOpt)]
pub struct InitBridge {
/// A bridge instance to initalize.
#[structopt(possible_values = &InitBridgeName::variants(), case_insensitive = true)]
bridge: InitBridgeName,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
}
// TODO [#851] Use kebab-case.
arg_enum! {
#[derive(Debug)]
/// Bridge to initialize.
pub enum InitBridgeName {
MillauToRialto,
RialtoToMillau,
WestendToMillau,
WestendToRococo,
RococoToWestend,
}
}
macro_rules! select_bridge {
($bridge: expr, $generic: tt) => {
match $bridge {
InitBridgeName::MillauToRialto => {
type Source = relay_millau_client::Millau;
type Target = relay_rialto_client::Rialto;
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
rialto_runtime::SudoCall::sudo(Box::new(
rialto_runtime::BridgeGrandpaMillauCall::initialize(init_data).into(),
))
.into()
}
$generic
}
InitBridgeName::RialtoToMillau => {
type Source = relay_rialto_client::Rialto;
type Target = relay_millau_client::Millau;
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
let initialize_call = millau_runtime::BridgeGrandpaRialtoCall::<
millau_runtime::Runtime,
millau_runtime::RialtoGrandpaInstance,
>::initialize(init_data);
millau_runtime::SudoCall::sudo(Box::new(initialize_call.into())).into()
}
$generic
}
InitBridgeName::WestendToMillau => {
type Source = relay_westend_client::Westend;
type Target = relay_millau_client::Millau;
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
// at Westend -> Millau initialization we're not using sudo, because otherwise our deployments
// may fail, because we need to initialize both Rialto -> Millau and Westend -> Millau bridge.
// => since there's single possible sudo account, one of transaction may fail with duplicate nonce error
millau_runtime::BridgeGrandpaWestendCall::<
millau_runtime::Runtime,
millau_runtime::WestendGrandpaInstance,
>::initialize(init_data)
.into()
}
$generic
}
InitBridgeName::WestendToRococo => {
type Source = relay_westend_client::Westend;
type Target = relay_rococo_client::Rococo;
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
bp_rococo::Call::BridgeGrandpaWestend(bp_rococo::BridgeGrandpaWestendCall::initialize(init_data))
}
$generic
}
InitBridgeName::RococoToWestend => {
type Source = relay_rococo_client::Rococo;
type Target = relay_westend_client::Westend;
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
bp_westend::Call::BridgeGrandpaRococo(bp_westend::BridgeGrandpaRococoCall::initialize(init_data))
}
$generic
}
}
};
}
impl InitBridge {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
select_bridge!(self.bridge, {
let source_client = self.source.to_client::<Source>().await?;
let target_client = self.target.to_client::<Target>().await?;
let target_sign = self.target_sign.to_keypair::<Target>()?;
crate::headers_initialize::initialize(
source_client,
target_client.clone(),
target_sign.public().into(),
move |transaction_nonce, initialization_data| {
Bytes(
Target::sign_transaction(
*target_client.genesis_hash(),
&target_sign,
transaction_nonce,
encode_init_bridge(initialization_data),
)
.encode(),
)
},
)
.await;
Ok(())
})
}
}
@@ -0,0 +1,444 @@
// 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/>.
//! Deal with CLI args of substrate-to-substrate relay.
use std::convert::TryInto;
use bp_messages::LaneId;
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use sp_runtime::app_crypto::Ss58Codec;
use structopt::{clap::arg_enum, StructOpt};
pub(crate) mod bridge;
pub(crate) mod encode_call;
pub(crate) mod encode_message;
pub(crate) mod estimate_fee;
pub(crate) mod send_message;
mod derive_account;
mod init_bridge;
mod relay_headers;
mod relay_headers_and_messages;
mod relay_messages;
/// Parse relay CLI args.
pub fn parse_args() -> Command {
Command::from_args()
}
/// Substrate-to-Substrate bridge utilities.
#[derive(StructOpt)]
#[structopt(about = "Substrate-to-Substrate relay")]
pub enum Command {
/// Start headers relay between two chains.
///
/// The on-chain bridge component should have been already initialized with
/// `init-bridge` sub-command.
RelayHeaders(relay_headers::RelayHeaders),
/// Start messages relay between two chains.
///
/// Ties up to `Messages` pallets on both chains and starts relaying messages.
/// Requires the header relay to be already running.
RelayMessages(relay_messages::RelayMessages),
/// Start headers and messages relay between two Substrate chains.
///
/// This high-level relay internally starts four low-level relays: two `RelayHeaders`
/// and two `RelayMessages` relays. Headers are only relayed when they are required by
/// the message relays - i.e. when there are messages or confirmations that needs to be
/// relayed between chains.
RelayHeadersAndMessages(relay_headers_and_messages::RelayHeadersAndMessages),
/// Initialize on-chain bridge pallet with current header data.
///
/// Sends initialization transaction to bootstrap the bridge with current finalized block data.
InitBridge(init_bridge::InitBridge),
/// Send custom message over the bridge.
///
/// Allows interacting with the bridge by sending messages over `Messages` component.
/// The message is being sent to the source chain, delivered to the target chain and dispatched
/// there.
SendMessage(send_message::SendMessage),
/// Generate SCALE-encoded `Call` for choosen network.
///
/// The call can be used either as message payload or can be wrapped into a transaction
/// and executed on the chain directly.
EncodeCall(encode_call::EncodeCall),
/// Generate SCALE-encoded `MessagePayload` object that can be sent over selected bridge.
///
/// The `MessagePayload` can be then fed to `Messages::send_message` function and sent over
/// the bridge.
EncodeMessage(encode_message::EncodeMessage),
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
EstimateFee(estimate_fee::EstimateFee),
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
DeriveAccount(derive_account::DeriveAccount),
}
impl Command {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
match self {
Self::RelayHeaders(arg) => arg.run().await?,
Self::RelayMessages(arg) => arg.run().await?,
Self::RelayHeadersAndMessages(arg) => arg.run().await?,
Self::InitBridge(arg) => arg.run().await?,
Self::SendMessage(arg) => arg.run().await?,
Self::EncodeCall(arg) => arg.run().await?,
Self::EncodeMessage(arg) => arg.run().await?,
Self::EstimateFee(arg) => arg.run().await?,
Self::DeriveAccount(arg) => arg.run().await?,
}
Ok(())
}
}
arg_enum! {
#[derive(Debug)]
/// The origin to use when dispatching the message on the target chain.
///
/// - `Target` uses account existing on the target chain (requires target private key).
/// - `Origin` uses account derived from the source-chain account.
pub enum Origins {
Target,
Source,
}
}
/// Generic balance type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Balance(pub u128);
impl std::fmt::Display for Balance {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
use num_format::{Locale, ToFormattedString};
write!(fmt, "{}", self.0.to_formatted_string(&Locale::en))
}
}
impl std::str::FromStr for Balance {
type Err = <u128 as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.parse()?))
}
}
impl Balance {
/// Cast balance to `u64` type, panicking if it's too large.
pub fn cast(&self) -> u64 {
self.0.try_into().expect("Balance is too high for this chain.")
}
}
/// Generic account id with custom parser.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountId {
account: sp_runtime::AccountId32,
ss58_format: sp_core::crypto::Ss58AddressFormat,
}
impl std::fmt::Display for AccountId {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", self.account.to_ss58check_with_version(self.ss58_format))
}
}
impl std::str::FromStr for AccountId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (account, ss58_format) = sp_runtime::AccountId32::from_ss58check_with_version(s)
.map_err(|err| format!("Unable to decode SS58 address: {:?}", err))?;
Ok(Self { account, ss58_format })
}
}
const SS58_FORMAT_PROOF: &str = "u16 -> Ss58Format is infallible; qed";
impl AccountId {
/// Create new SS58-formatted address from raw account id.
pub fn from_raw<T: CliChain>(account: sp_runtime::AccountId32) -> Self {
Self {
account,
ss58_format: T::ss58_format().try_into().expect(SS58_FORMAT_PROOF),
}
}
/// Enforces formatting account to be for given [`CliChain`] type.
///
/// This will change the `ss58format` of the account to match the requested one.
/// Note that a warning will be produced in case the current format does not match
/// the requested one, but the conversion always succeeds.
pub fn enforce_chain<T: CliChain>(&mut self) {
let original = self.clone();
self.ss58_format = T::ss58_format().try_into().expect(SS58_FORMAT_PROOF);
log::debug!("{} SS58 format: {} (RAW: {})", self, self.ss58_format, self.account);
if original.ss58_format != self.ss58_format {
log::warn!(
target: "bridge",
"Address {} does not seem to match {}'s SS58 format (got: {}, expected: {}).\nConverted to: {}",
original,
T::NAME,
original.ss58_format,
self.ss58_format,
self,
)
}
}
/// Returns the raw (no SS58-prefixed) account id.
pub fn raw_id(&self) -> sp_runtime::AccountId32 {
self.account.clone()
}
}
/// Bridge-supported network definition.
///
/// Used to abstract away CLI commands.
pub trait CliChain: relay_substrate_client::Chain {
/// Chain's current version of the runtime.
const RUNTIME_VERSION: sp_version::RuntimeVersion;
/// Crypto keypair type used to send messages.
///
/// In case of chains supporting multiple cryptos, pick one used by the CLI.
type KeyPair: sp_core::crypto::Pair;
/// Bridge Message Payload type.
///
/// TODO [#854] This should be removed in favour of target-specifc types.
type MessagePayload;
/// Numeric value of SS58 format.
fn ss58_format() -> u16;
/// Construct message payload to be sent over the bridge.
fn encode_message(message: crate::cli::encode_message::MessagePayload) -> Result<Self::MessagePayload, String>;
/// Maximal extrinsic weight (from the runtime).
fn max_extrinsic_weight() -> Weight;
}
/// Lane id.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HexLaneId(pub LaneId);
impl From<HexLaneId> for LaneId {
fn from(lane_id: HexLaneId) -> LaneId {
lane_id.0
}
}
impl std::str::FromStr for HexLaneId {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lane_id = LaneId::default();
hex::decode_to_slice(s, &mut lane_id)?;
Ok(HexLaneId(lane_id))
}
}
/// Nicer formatting for raw bytes vectors.
#[derive(Default, Encode, Decode, PartialEq, Eq)]
pub struct HexBytes(pub Vec<u8>);
impl std::str::FromStr for HexBytes {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hex::decode(s)?))
}
}
impl std::fmt::Debug for HexBytes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "0x{}", self)
}
}
impl std::fmt::Display for HexBytes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", hex::encode(&self.0))
}
}
impl HexBytes {
/// Encode given object and wrap into nicely formatted bytes.
pub fn encode<T: Encode>(t: &T) -> Self {
Self(t.encode())
}
}
/// Prometheus metrics params.
#[derive(StructOpt)]
pub struct PrometheusParams {
/// Do not expose a Prometheus metric endpoint.
#[structopt(long)]
pub no_prometheus: bool,
/// Expose Prometheus endpoint at given interface.
#[structopt(long, default_value = "127.0.0.1")]
pub prometheus_host: String,
/// Expose Prometheus endpoint at given port.
#[structopt(long, default_value = "9616")]
pub prometheus_port: u16,
}
impl From<PrometheusParams> for relay_utils::metrics::MetricsParams {
fn from(cli_params: PrometheusParams) -> relay_utils::metrics::MetricsParams {
if !cli_params.no_prometheus {
Some(relay_utils::metrics::MetricsAddress {
host: cli_params.prometheus_host,
port: cli_params.prometheus_port,
})
.into()
} else {
None.into()
}
}
}
/// Either explicit or maximal allowed value.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExplicitOrMaximal<V> {
/// User has explicitly specified argument value.
Explicit(V),
/// Maximal allowed value for this argument.
Maximal,
}
impl<V: std::str::FromStr> std::str::FromStr for ExplicitOrMaximal<V>
where
V::Err: std::fmt::Debug,
{
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "max" {
return Ok(ExplicitOrMaximal::Maximal);
}
V::from_str(s)
.map(ExplicitOrMaximal::Explicit)
.map_err(|e| format!("Failed to parse '{:?}'. Expected 'max' or explicit value", e))
}
}
/// Create chain-specific set of configuration objects: connection parameters,
/// signing parameters and bridge initialisation parameters.
#[macro_export]
macro_rules! declare_chain_options {
($chain:ident, $chain_prefix:ident) => {
paste::item! {
#[doc = $chain " connection params."]
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub struct [<$chain ConnectionParams>] {
#[doc = "Connect to " $chain " node at given host."]
#[structopt(long, default_value = "127.0.0.1")]
pub [<$chain_prefix _host>]: String,
#[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."]
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub struct [<$chain SigningParams>] {
#[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _signer>]: String,
#[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _signer_password>]: Option<String>,
}
impl [<$chain SigningParams>] {
/// Parse signing params into chain-specific KeyPair.
pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Chain::KeyPair> {
use sp_core::crypto::Pair;
Chain::KeyPair::from_string(
&self.[<$chain_prefix _signer>],
self.[<$chain_prefix _signer_password>].as_deref()
).map_err(|e| anyhow::format_err!("{:?}", e))
}
}
impl [<$chain ConnectionParams>] {
/// Convert connection params into Substrate client.
pub async fn to_client<Chain: CliChain>(
&self,
) -> anyhow::Result<relay_substrate_client::Client<Chain>> {
Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams {
host: self.[<$chain_prefix _host>].clone(),
port: self.[<$chain_prefix _port>],
secure: self.[<$chain_prefix _secure>],
})
.await?
)
}
}
}
};
}
declare_chain_options!(Source, source);
declare_chain_options!(Target, target);
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn should_format_addresses_with_ss58_format() {
// given
let rialto1 = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU";
let rialto2 = "5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2";
let millau1 = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9";
let millau2 = "74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5";
let expected = vec![rialto1, rialto2, millau1, millau2];
// when
let parsed = expected
.iter()
.map(|s| AccountId::from_str(s).unwrap())
.collect::<Vec<_>>();
let actual = parsed.iter().map(|a| format!("{}", a)).collect::<Vec<_>>();
assert_eq!(actual, expected)
}
#[test]
fn hex_bytes_display_matches_from_str_for_clap() {
// given
let hex = HexBytes(vec![1, 2, 3, 4]);
let display = format!("{}", hex);
// when
let hex2: HexBytes = display.parse().unwrap();
// then
assert_eq!(hex.0, hex2.0);
}
}
@@ -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/>.
use crate::cli::{PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams};
use crate::finality_pipeline::SubstrateFinalitySyncPipeline;
use structopt::{clap::arg_enum, StructOpt};
/// Start headers relayer process.
#[derive(StructOpt)]
pub struct RelayHeaders {
/// A bridge instance to relay headers for.
#[structopt(possible_values = &RelayHeadersBridge::variants(), case_insensitive = true)]
bridge: RelayHeadersBridge,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
// TODO [#851] Use kebab-case.
arg_enum! {
#[derive(Debug)]
/// Headers relay bridge.
pub enum RelayHeadersBridge {
MillauToRialto,
RialtoToMillau,
WestendToMillau,
WestendToRococo,
RococoToWestend,
}
}
macro_rules! select_bridge {
($bridge: expr, $generic: tt) => {
match $bridge {
RelayHeadersBridge::MillauToRialto => {
type Source = relay_millau_client::Millau;
type Target = relay_rialto_client::Rialto;
type Finality = crate::chains::millau_headers_to_rialto::MillauFinalityToRialto;
$generic
}
RelayHeadersBridge::RialtoToMillau => {
type Source = relay_rialto_client::Rialto;
type Target = relay_millau_client::Millau;
type Finality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau;
$generic
}
RelayHeadersBridge::WestendToMillau => {
type Source = relay_westend_client::Westend;
type Target = relay_millau_client::Millau;
type Finality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau;
$generic
}
RelayHeadersBridge::WestendToRococo => {
type Source = relay_westend_client::Westend;
type Target = relay_rococo_client::Rococo;
type Finality = crate::chains::westend_headers_to_rococo::WestendFinalityToRococo;
$generic
}
RelayHeadersBridge::RococoToWestend => {
type Source = relay_rococo_client::Rococo;
type Target = relay_westend_client::Westend;
type Finality = crate::chains::rococo_headers_to_westend::RococoFinalityToWestend;
$generic
}
}
};
}
impl RelayHeaders {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
select_bridge!(self.bridge, {
let source_client = self.source.to_client::<Source>().await?;
let target_client = self.target.to_client::<Target>().await?;
let target_sign = self.target_sign.to_keypair::<Target>()?;
let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?;
crate::finality_pipeline::run(
Finality::new(target_client.clone(), target_sign),
source_client,
target_client,
metrics_params,
)
.await
})
}
}
@@ -0,0 +1,183 @@
// 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/>.
//! Complex headers+messages relays support.
//!
//! To add new complex relay between `ChainA` and `ChainB`, you must:
//!
//! 1) ensure that there's a `declare_chain_options!(...)` for both chains;
//! 2) add `declare_bridge_options!(...)` for the bridge;
//! 3) add bridge support to the `select_bridge! { ... }` macro.
use crate::cli::{CliChain, HexLaneId, PrometheusParams};
use crate::declare_chain_options;
use crate::messages_lane::MessagesRelayParams;
use crate::on_demand_headers::OnDemandHeadersRelay;
use futures::{FutureExt, TryFutureExt};
use relay_utils::metrics::MetricsParams;
use structopt::StructOpt;
/// Start headers+messages relayer process.
#[derive(StructOpt)]
pub enum RelayHeadersAndMessages {
MillauRialto(MillauRialtoHeadersAndMessages),
}
/// Parameters that have the same names across all bridges.
#[derive(StructOpt)]
pub struct HeadersAndMessagesSharedParams {
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
// The reason behind this macro is that 'normal' relays are using source and target chains terminology,
// which is unusable for both-way relays (if you're relaying headers from Rialto to Millau and from
// Millau to Rialto, then which chain is source?).
macro_rules! declare_bridge_options {
($chain1:ident, $chain2:ident) => {
paste::item! {
#[doc = $chain1 " and " $chain2 " headers+messages relay params."]
#[derive(StructOpt)]
pub struct [<$chain1 $chain2 HeadersAndMessages>] {
#[structopt(flatten)]
shared: HeadersAndMessagesSharedParams,
#[structopt(flatten)]
left: [<$chain1 ConnectionParams>],
#[structopt(flatten)]
left_sign: [<$chain1 SigningParams>],
#[structopt(flatten)]
right: [<$chain2 ConnectionParams>],
#[structopt(flatten)]
right_sign: [<$chain2 SigningParams>],
}
#[allow(unreachable_patterns)]
impl From<RelayHeadersAndMessages> for [<$chain1 $chain2 HeadersAndMessages>] {
fn from(relay_params: RelayHeadersAndMessages) -> [<$chain1 $chain2 HeadersAndMessages>] {
match relay_params {
RelayHeadersAndMessages::[<$chain1 $chain2>](params) => params,
_ => unreachable!(),
}
}
}
}
};
}
macro_rules! select_bridge {
($bridge: expr, $generic: tt) => {
match $bridge {
RelayHeadersAndMessages::MillauRialto(_) => {
type Params = MillauRialtoHeadersAndMessages;
type Left = relay_millau_client::Millau;
type Right = relay_rialto_client::Rialto;
type LeftToRightFinality = crate::chains::millau_headers_to_rialto::MillauFinalityToRialto;
type RightToLeftFinality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau;
type LeftToRightMessages = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
type RightToLeftMessages = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau;
use crate::chains::millau_messages_to_rialto::run as left_to_right_messages;
use crate::chains::rialto_messages_to_millau::run as right_to_left_messages;
$generic
}
}
};
}
// All supported chains.
declare_chain_options!(Millau, millau);
declare_chain_options!(Rialto, rialto);
// All supported bridges.
declare_bridge_options!(Millau, Rialto);
impl RelayHeadersAndMessages {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
select_bridge!(self, {
let params: Params = self.into();
let left_client = params.left.to_client::<Left>().await?;
let left_sign = params.left_sign.to_keypair::<Left>()?;
let right_client = params.right.to_client::<Right>().await?;
let right_sign = params.right_sign.to_keypair::<Right>()?;
let lane = params.shared.lane.into();
let metrics_params: MetricsParams = params.shared.prometheus_params.into();
let metrics_params = relay_utils::relay_metrics(None, metrics_params).into_params();
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new(
left_client.clone(),
right_client.clone(),
LeftToRightFinality::new(right_client.clone(), right_sign.clone()),
);
let right_to_left_on_demand_headers = OnDemandHeadersRelay::new(
right_client.clone(),
left_client.clone(),
RightToLeftFinality::new(left_client.clone(), left_sign.clone()),
);
let left_to_right_messages = left_to_right_messages(MessagesRelayParams {
source_client: left_client.clone(),
source_sign: left_sign.clone(),
target_client: right_client.clone(),
target_sign: right_sign.clone(),
source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()),
target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()),
lane_id: lane,
metrics_params: metrics_params
.clone()
.disable()
.metrics_prefix(messages_relay::message_lane_loop::metrics_prefix::<LeftToRightMessages>(&lane)),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
let right_to_left_messages = right_to_left_messages(MessagesRelayParams {
source_client: right_client,
source_sign: right_sign,
target_client: left_client.clone(),
target_sign: left_sign.clone(),
source_to_target_headers_relay: Some(right_to_left_on_demand_headers),
target_to_source_headers_relay: Some(left_to_right_on_demand_headers),
lane_id: lane,
metrics_params: metrics_params
.clone()
.disable()
.metrics_prefix(messages_relay::message_lane_loop::metrics_prefix::<RightToLeftMessages>(&lane)),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
relay_utils::relay_metrics(None, metrics_params)
.expose()
.await
.map_err(|e| anyhow::format_err!("{}", e))?;
futures::future::select(left_to_right_messages, right_to_left_messages)
.await
.factor_first()
.0
})
}
}
@@ -0,0 +1,71 @@
// 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/>.
use crate::cli::bridge::FullBridge;
use crate::cli::{
HexLaneId, PrometheusParams, SourceConnectionParams, SourceSigningParams, TargetConnectionParams,
TargetSigningParams,
};
use crate::messages_lane::MessagesRelayParams;
use crate::select_full_bridge;
use structopt::StructOpt;
/// Start messages relayer process.
#[derive(StructOpt)]
pub struct RelayMessages {
/// A bridge instance to relay messages for.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
impl RelayMessages {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
select_full_bridge!(self.bridge, {
let source_client = self.source.to_client::<Source>().await?;
let source_sign = self.source_sign.to_keypair::<Source>()?;
let target_client = self.target.to_client::<Target>().await?;
let target_sign = self.target_sign.to_keypair::<Target>()?;
relay_messages(MessagesRelayParams {
source_client,
source_sign,
target_client,
target_sign,
source_to_target_headers_relay: None,
target_to_source_headers_relay: None,
lane_id: self.lane.into(),
metrics_params: self.prometheus_params.into(),
})
.await
.map_err(|e| anyhow::format_err!("{}", e))
})
}
}
@@ -0,0 +1,317 @@
// 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/>.
use crate::cli::bridge::FullBridge;
use crate::cli::encode_call::{self, CliEncodeCall};
use crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee;
use crate::cli::{
Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams, SourceSigningParams,
TargetSigningParams,
};
use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use pallet_bridge_dispatch::{CallOrigin, MessagePayload};
use relay_substrate_client::{Chain, TransactionSignScheme};
use sp_core::{Bytes, Pair};
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
use std::fmt::Debug;
use structopt::StructOpt;
/// Send bridge message.
#[derive(StructOpt)]
pub struct SendMessage {
/// A bridge instance to encode call for.
#[structopt(possible_values = &FullBridge::variants(), case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
// TODO [#885] Move TargetSign to origins
#[structopt(flatten)]
target_sign: TargetSigningParams,
/// Hex-encoded lane id. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// Dispatch weight of the message. If not passed, determined automatically.
#[structopt(long)]
dispatch_weight: Option<ExplicitOrMaximal<Weight>>,
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined automatically.
#[structopt(long)]
fee: Option<Balance>,
/// Message type.
#[structopt(subcommand)]
message: crate::cli::encode_call::Call,
/// The origin to use when dispatching the message on the target chain. Defaults to
/// `SourceAccount`.
#[structopt(long, possible_values = &Origins::variants(), default_value = "Source")]
origin: Origins,
}
impl SendMessage {
pub fn encode_payload(
&mut self,
) -> anyhow::Result<MessagePayload<AccountId32, MultiSigner, MultiSignature, Vec<u8>>> {
crate::select_full_bridge!(self.bridge, {
let SendMessage {
source_sign,
target_sign,
ref mut message,
dispatch_weight,
origin,
bridge,
..
} = self;
let source_sign = source_sign.to_keypair::<Source>()?;
let target_sign = target_sign.to_keypair::<Target>()?;
encode_call::preprocess_call::<Source, Target>(message, bridge.bridge_instance_index());
let target_call = Target::encode_call(&message)?;
let payload = {
let target_call_weight = prepare_call_dispatch_weight(
dispatch_weight,
ExplicitOrMaximal::Explicit(target_call.get_dispatch_info().weight),
compute_maximal_message_dispatch_weight(Target::max_extrinsic_weight()),
);
let source_sender_public: MultiSigner = source_sign.public().into();
let source_account_id = source_sender_public.into_account();
message_payload(
Target::RUNTIME_VERSION.spec_version,
target_call_weight,
match origin {
Origins::Source => CallOrigin::SourceAccount(source_account_id),
Origins::Target => {
let digest = account_ownership_digest(
&target_call,
source_account_id.clone(),
Target::RUNTIME_VERSION.spec_version,
);
let target_origin_public = target_sign.public();
let digest_signature = target_sign.sign(&digest);
CallOrigin::TargetAccount(
source_account_id,
target_origin_public.into(),
digest_signature.into(),
)
}
},
&target_call,
)
};
Ok(payload)
})
}
/// Run the command.
pub async fn run(mut self) -> anyhow::Result<()> {
crate::select_full_bridge!(self.bridge, {
let payload = self.encode_payload()?;
let source_client = self.source.to_client::<Source>().await?;
let source_sign = self.source_sign.to_keypair::<Source>()?;
let lane = self.lane.clone().into();
let fee = match self.fee {
Some(fee) => fee,
None => Balance(
estimate_message_delivery_and_dispatch_fee::<
<Source as relay_substrate_client::ChainWithBalances>::NativeBalance,
_,
_,
>(&source_client, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload.clone())
.await? as _,
),
};
let dispatch_weight = payload.weight;
let send_message_call = Source::encode_call(&encode_call::Call::BridgeSendMessage {
bridge_instance_index: self.bridge.bridge_instance_index(),
lane: self.lane,
payload: HexBytes::encode(&payload),
fee,
})?;
source_client
.submit_signed_extrinsic(source_sign.public().into(), |transaction_nonce| {
let signed_source_call = Source::sign_transaction(
*source_client.genesis_hash(),
&source_sign,
transaction_nonce,
send_message_call,
)
.encode();
log::info!(
target: "bridge",
"Sending message to {}. Size: {}. Dispatch weight: {}. Fee: {}",
Target::NAME,
signed_source_call.len(),
dispatch_weight,
fee,
);
log::info!(
target: "bridge",
"Signed {} Call: {:?}",
Source::NAME,
HexBytes::encode(&signed_source_call)
);
Bytes(signed_source_call)
})
.await?;
});
Ok(())
}
}
fn prepare_call_dispatch_weight(
user_specified_dispatch_weight: &Option<ExplicitOrMaximal<Weight>>,
weight_from_pre_dispatch_call: ExplicitOrMaximal<Weight>,
maximal_allowed_weight: Weight,
) -> Weight {
match user_specified_dispatch_weight
.clone()
.unwrap_or(weight_from_pre_dispatch_call)
{
ExplicitOrMaximal::Explicit(weight) => weight,
ExplicitOrMaximal::Maximal => maximal_allowed_weight,
}
}
pub(crate) fn message_payload<SAccountId, TPublic, TSignature>(
spec_version: u32,
weight: Weight,
origin: CallOrigin<SAccountId, TPublic, TSignature>,
call: &impl Encode,
) -> MessagePayload<SAccountId, TPublic, TSignature, Vec<u8>>
where
SAccountId: Encode + Debug,
TPublic: Encode + Debug,
TSignature: Encode + Debug,
{
// Display nicely formatted call.
let payload = MessagePayload {
spec_version,
weight,
origin,
call: HexBytes::encode(call),
};
log::info!(target: "bridge", "Created Message Payload: {:#?}", payload);
log::info!(target: "bridge", "Encoded Message Payload: {:?}", HexBytes::encode(&payload));
// re-pack to return `Vec<u8>`
let MessagePayload {
spec_version,
weight,
origin,
call,
} = payload;
MessagePayload {
spec_version,
weight,
origin,
call: call.0,
}
}
pub(crate) fn compute_maximal_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
bridge_runtime_common::messages::target::maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight)
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
#[test]
fn send_remark_rialto_to_millau() {
// given
let mut send_message = SendMessage::from_iter(vec![
"send-message",
"RialtoToMillau",
"--source-port",
"1234",
"--source-signer",
"//Alice",
"--target-signer",
"//Bob",
"remark",
"--remark-payload",
"1234",
]);
// when
let payload = send_message.encode_payload().unwrap();
// then
assert_eq!(
payload,
MessagePayload {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 1345000,
origin: CallOrigin::SourceAccount(sp_keyring::AccountKeyring::Alice.to_account_id()),
call: hex!("0401081234").to_vec(),
}
);
}
#[test]
fn send_remark_millau_to_rialto() {
// given
let mut send_message = SendMessage::from_iter(vec![
"send-message",
"MillauToRialto",
"--source-port",
"1234",
"--source-signer",
"//Alice",
"--origin",
"Target",
"--target-signer",
"//Bob",
"remark",
"--remark-payload",
"1234",
]);
// when
let payload = send_message.encode_payload().unwrap();
// then
// Since signatures are randomized we extract it from here and only check the rest.
let signature = match payload.origin {
CallOrigin::TargetAccount(_, _, ref sig) => sig.clone(),
_ => panic!("Unexpected `CallOrigin`: {:?}", payload),
};
assert_eq!(
payload,
MessagePayload {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 1345000,
origin: CallOrigin::TargetAccount(
sp_keyring::AccountKeyring::Alice.to_account_id(),
sp_keyring::AccountKeyring::Bob.into(),
signature,
),
call: hex!("0701081234").to_vec(),
}
);
}
}