diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 7f78bc58213d..22df99bbb371 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -55,6 +55,7 @@ pub use frame_support::{ }; pub use pallet_balances::Call as BalancesCall; +pub use pallet_message_lane::Call as MessageLaneCall; pub use pallet_substrate_bridge::Call as BridgeSubstrateCall; pub use pallet_timestamp::Call as TimestampCall; diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 008e47373169..778b2afd18e9 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -60,6 +60,7 @@ pub use frame_support::{ StorageValue, }; +pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_bridge_currency_exchange::Call as BridgeCurrencyExchangeCall; pub use pallet_bridge_eth_poa::Call as BridgeEthPoACall; diff --git a/bridges/relays/millau-client/Cargo.toml b/bridges/relays/millau-client/Cargo.toml index 3bf0dc426f05..bf6569d7eed3 100644 --- a/bridges/relays/millau-client/Cargo.toml +++ b/bridges/relays/millau-client/Cargo.toml @@ -18,4 +18,6 @@ millau-runtime = { path = "../../bin/millau/runtime" } # Substrate Dependencies frame-system = "2.0" +pallet-transaction-payment = "2.0" +sp-core = "2.0" sp-runtime = "2.0" diff --git a/bridges/relays/millau-client/src/lib.rs b/bridges/relays/millau-client/src/lib.rs index c1fadba024da..129602cf7a46 100644 --- a/bridges/relays/millau-client/src/lib.rs +++ b/bridges/relays/millau-client/src/lib.rs @@ -16,10 +16,14 @@ //! Types used to connect to the Millau-Substrate chain. -use relay_substrate_client::{Chain, ChainBase}; - +use codec::Encode; use headers_relay::sync_types::SourceHeader; -use sp_runtime::traits::Header as HeaderT; +use relay_substrate_client::{Chain, ChainBase, Client, TransactionSignScheme}; +use sp_core::Pair; +use sp_runtime::{ + generic::SignedPayload, + traits::{Header as HeaderT, IdentifyAccount}, +}; /// Millau header id. pub type HeaderId = relay_utils::HeaderId; @@ -42,6 +46,68 @@ impl Chain for Millau { type Call = millau_runtime::Call; } +impl TransactionSignScheme for Millau { + type Chain = Millau; + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = millau_runtime::UncheckedExtrinsic; + + fn sign_transaction( + client: &Client, + signer: &Self::AccountKeyPair, + signer_nonce: ::Index, + call: ::Call, + ) -> Self::SignedTransaction { + let raw_payload = SignedPayload::from_raw( + call, + ( + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::Immortal), + frame_system::CheckNonce::::from(signer_nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ), + ( + millau_runtime::VERSION.spec_version, + millau_runtime::VERSION.transaction_version, + *client.genesis_hash(), + *client.genesis_hash(), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let signer: sp_runtime::MultiSigner = signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + millau_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra) + } +} + +/// Millau signing params. +#[derive(Clone)] +pub struct SigningParams { + /// Substrate transactions signer. + pub signer: sp_core::sr25519::Pair, +} + +impl SigningParams { + /// Create signing params from SURI and password. + pub fn from_suri(suri: &str, password: Option<&str>) -> Result { + Ok(SigningParams { + signer: sp_core::sr25519::Pair::from_string(suri, password)?, + }) + } +} + +impl std::fmt::Debug for SigningParams { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.signer.public()) + } +} + /// Millau header type used in headers sync. #[derive(Clone, Debug, PartialEq)] pub struct SyncHeader(millau_runtime::Header); diff --git a/bridges/relays/substrate/Cargo.toml b/bridges/relays/substrate/Cargo.toml index a3f6ca6e435c..3a760ffde86b 100644 --- a/bridges/relays/substrate/Cargo.toml +++ b/bridges/relays/substrate/Cargo.toml @@ -10,23 +10,29 @@ async-std = "1.6.2" async-trait = "0.1.41" codec = { package = "parity-scale-codec", version = "1.3.4" } futures = "0.3.7" +hex = "0.4" log = "0.4.11" paste = "1.0" structopt = "0.3" # Bridge dependencies +bp-message-lane = { path = "../../primitives/message-lane" } bp-millau = { path = "../../primitives/millau" } bp-rialto = { path = "../../primitives/rialto" } headers-relay = { path = "../headers-relay" } messages-relay = { path = "../messages-relay" } +millau-runtime = { path = "../../bin/millau/runtime" } +pallet-bridge-call-dispatch = { path = "../../modules/call-dispatch" } pallet-substrate-bridge = { path = "../../modules/substrate" } relay-millau-client = { path = "../millau-client" } relay-rialto-client = { path = "../rialto-client" } relay-substrate-client = { path = "../substrate-client" } relay-utils = { path = "../utils" } +rialto-runtime = { path = "../../bin/rialto/runtime" } # Substrate Dependencies +frame-support = "2.0" sp-core = "2.0" sp-runtime = "2.0" diff --git a/bridges/relays/substrate/src/cli.rs b/bridges/relays/substrate/src/cli.rs index e13eccf6016d..ec081cd1d278 100644 --- a/bridges/relays/substrate/src/cli.rs +++ b/bridges/relays/substrate/src/cli.rs @@ -16,7 +16,8 @@ //! Deal with CLI args of substrate-to-substrate relay. -use structopt::StructOpt; +use bp_message_lane::LaneId; +use structopt::{clap::arg_enum, StructOpt}; /// Parse relay CLI args. pub fn parse_args() -> Command { @@ -38,6 +39,52 @@ pub enum Command { #[structopt(flatten)] prometheus_params: PrometheusParams, }, + /// Submit message to given Rialto -> Millau lane. + SubmitMillauToRialtoMessage { + #[structopt(flatten)] + millau: MillauConnectionParams, + #[structopt(flatten)] + millau_sign: MillauSigningParams, + #[structopt(flatten)] + rialto_sign: RialtoSigningParams, + /// Hex-encoded lane id. + #[structopt(long)] + lane: HexLaneId, + /// Message type. + #[structopt(long, possible_values = &ToRialtoMessage::variants())] + message: ToRialtoMessage, + /// Delivery and dispatch fee. + #[structopt(long)] + fee: bp_millau::Balance, + }, +} + +arg_enum! { + #[derive(Debug)] + /// All possible messages that may be delivered to the Rialto chain. + pub enum ToRialtoMessage { + Remark, + } +} + +/// Lane id. +#[derive(Debug)] +pub struct HexLaneId(LaneId); + +impl From 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 { + let mut lane_id = LaneId::default(); + hex::decode_to_slice(s, &mut lane_id)?; + Ok(HexLaneId(lane_id)) + } } /// Prometheus metrics params. diff --git a/bridges/relays/substrate/src/main.rs b/bridges/relays/substrate/src/main.rs index 53f10dab86d6..76b2aa9189dd 100644 --- a/bridges/relays/substrate/src/main.rs +++ b/bridges/relays/substrate/src/main.rs @@ -18,14 +18,19 @@ #![warn(missing_docs)] -use relay_rialto_client::SigningParams as RialtoSigningParams; -use relay_substrate_client::ConnectionParams; +use codec::Encode; +use frame_support::weights::GetDispatchInfo; +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::{ConnectionParams, TransactionSignScheme}; use relay_utils::initialize::initialize_relay; +use sp_core::{Bytes, Pair}; /// Millau node client. -pub type MillauClient = relay_substrate_client::Client; +pub type MillauClient = relay_substrate_client::Client; /// Rialto node client. -pub type RialtoClient = relay_substrate_client::Client; +pub type RialtoClient = relay_substrate_client::Client; mod cli; mod headers_maintain; @@ -66,6 +71,80 @@ async fn run_command(command: cli::Command) -> Result<(), String> { .map_err(|e| format!("Failed to parse rialto-signer: {:?}", e))?; millau_headers_to_rialto::run(millau_client, rialto_client, rialto_sign, prometheus_params.into()).await; } + cli::Command::SubmitMillauToRialtoMessage { + millau, + millau_sign, + rialto_sign, + lane, + message, + fee, + } => { + let millau_client = MillauClient::new(ConnectionParams { + host: millau.millau_host, + port: millau.millau_port, + }) + .await?; + let millau_sign = MillauSigningParams::from_suri( + &millau_sign.millau_signer, + millau_sign.millau_signer_password.as_deref(), + ) + .map_err(|e| format!("Failed to parse millau-signer: {:?}", e))?; + let rialto_sign = RialtoSigningParams::from_suri( + &rialto_sign.rialto_signer, + rialto_sign.rialto_signer_password.as_deref(), + ) + .map_err(|e| format!("Failed to parse rialto-signer: {:?}", e))?; + + let rialto_call = match message { + cli::ToRialtoMessage::Remark => rialto_runtime::Call::System(rialto_runtime::SystemCall::remark( + format!( + "Unix time: {}", + std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(), + ) + .as_bytes() + .to_vec(), + )), + }; + let rialto_call_weight = rialto_call.get_dispatch_info().weight; + + let millau_sender_public = millau_sign.signer.public(); + let rialto_origin_public = rialto_sign.signer.public(); + + let mut rialto_origin_signature_message = Vec::new(); + rialto_call.encode_to(&mut rialto_origin_signature_message); + millau_sender_public.encode_to(&mut rialto_origin_signature_message); + let rialto_origin_signature = rialto_sign.signer.sign(&rialto_origin_signature_message); + + let millau_call = + millau_runtime::Call::BridgeRialtoMessageLane(millau_runtime::MessageLaneCall::send_message( + lane.into(), + MessagePayload { + spec_version: millau_runtime::VERSION.spec_version, + weight: rialto_call_weight, + origin: CallOrigin::RealAccount( + millau_sender_public.into(), + rialto_origin_public.into(), + rialto_origin_signature.into(), + ), + call: rialto_call.encode(), + }, + fee, + )); + + let signed_millau_call = Millau::sign_transaction( + &millau_client, + &millau_sign.signer, + millau_client.next_account_index(millau_sender_public.into()).await?, + millau_call, + ); + + millau_client + .submit_extrinsic(Bytes(signed_millau_call.encode())) + .await?; + } } Ok(())