// 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 .
use crate::{
chains::{
millau_headers_to_rialto::MillauToRialtoCliBridge,
millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge,
rialto_headers_to_millau::RialtoToMillauCliBridge,
rialto_parachains_to_millau::RialtoParachainToMillauCliBridge,
},
cli::{
bridge::{FullBridge, MessagesCliBridge},
chain_schema::*,
encode_message::{self, CliEncodeMessage, RawMessage},
CliChain,
},
};
use async_trait::async_trait;
use codec::{Decode, Encode};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, UnsignedTransaction,
};
use sp_core::Pair;
use sp_runtime::AccountId32;
use std::fmt::Display;
use structopt::StructOpt;
use strum::VariantNames;
/// 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,
/// Message type.
#[structopt(subcommand)]
message: crate::cli::encode_message::Message,
}
#[async_trait]
trait MessageSender: MessagesCliBridge
where
Self::Source: ChainBase + ChainWithTransactions + CliChain + CliEncodeMessage,
::Balance: Display + From + Into,
::Call: Sync,
::SignedTransaction: Sync,
AccountIdOf: From< as Pair>::Public>,
AccountId32: From< as Pair>::Public>,
{
async fn send_message(data: SendMessage) -> anyhow::Result<()> {
let payload = encode_message::encode_message::(&data.message)?;
let source_client = data.source.into_client::().await?;
let source_sign = data.source_sign.to_keypair::()?;
let payload_len = payload.encoded_size();
let send_message_call = Self::Source::encode_send_xcm(
decode_xcm(payload)?,
data.bridge.bridge_instance_index(),
)?;
source_client
.submit_signed_extrinsic(&source_sign, move |_, transaction_nonce| {
let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce);
log::info!(
target: "bridge",
"Sending message to {}. Size: {}",
Self::Target::NAME,
payload_len,
);
Ok(unsigned)
})
.await?;
Ok(())
}
}
impl MessageSender for MillauToRialtoCliBridge {}
impl MessageSender for RialtoToMillauCliBridge {}
impl MessageSender for MillauToRialtoParachainCliBridge {}
impl MessageSender for RialtoParachainToMillauCliBridge {}
impl SendMessage {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
match self.bridge {
FullBridge::MillauToRialto => MillauToRialtoCliBridge::send_message(self),
FullBridge::RialtoToMillau => RialtoToMillauCliBridge::send_message(self),
FullBridge::MillauToRialtoParachain =>
MillauToRialtoParachainCliBridge::send_message(self),
FullBridge::RialtoParachainToMillau =>
RialtoParachainToMillauCliBridge::send_message(self),
FullBridge::BridgeHubRococoToBridgeHubWococo => unimplemented!(
"Sending message from BridgeHubRococo to BridgeHubWococo is not supported"
),
FullBridge::BridgeHubWococoToBridgeHubRococo => unimplemented!(
"Sending message from BridgeHubWococo to BridgeHubRococo is not supported"
),
}
.await
}
}
/// Decode SCALE encoded raw XCM message.
pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result> {
Decode::decode(&mut &message[..])
.map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::{ExplicitOrMaximal, HexBytes};
#[test]
fn send_raw_rialto_to_millau() {
// given
let send_message = SendMessage::from_iter(vec![
"send-message",
"rialto-to-millau",
"--source-port",
"1234",
"--source-signer",
"//Alice",
"raw",
"dead",
]);
// then
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) }
);
}
#[test]
fn send_sized_rialto_to_millau() {
// given
let send_message = SendMessage::from_iter(vec![
"send-message",
"rialto-to-millau",
"--source-port",
"1234",
"--source-signer",
"//Alice",
"sized",
"max",
]);
// then
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal }
);
}
}