Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions bin/runtime-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,21 @@ pub enum CustomNetworkId {
RialtoParachain,
}

impl TryFrom<bp_runtime::ChainId> for CustomNetworkId {
type Error = ();

fn try_from(chain: bp_runtime::ChainId) -> Result<Self, Self::Error> {
Ok(match chain {
bp_runtime::MILLAU_CHAIN_ID => Self::Millau,
bp_runtime::RIALTO_CHAIN_ID => Self::Rialto,
bp_runtime::RIALTO_PARACHAIN_CHAIN_ID => Self::RialtoParachain,
_ => return Err(()),
})
}
}

impl CustomNetworkId {
/// Converts self to XCM' network id.
pub const fn as_network_id(&self) -> NetworkId {
match *self {
CustomNetworkId::Millau => NetworkId::Kusama,
Expand Down
7 changes: 5 additions & 2 deletions relays/bin-substrate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ relay-utils = { path = "../utils" }
relay-westend-client = { path = "../client-westend" }
relay-wococo-client = { path = "../client-wococo" }
rialto-runtime = { path = "../../bin/rialto/runtime" }
# we are not using this runtime to craft callsour transactions, but we still need it
# to prepare large XCM messages
rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" }
substrate-relay-helper = { path = "../lib-substrate-relay" }

# Substrate Dependencies
Expand All @@ -62,8 +65,8 @@ polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch =
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false }

xcm = { git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master" }

[dev-dependencies]
bp-test-utils = { path = "../../primitives/test-utils" }
Expand Down
27 changes: 26 additions & 1 deletion relays/bin-substrate/src/chains/millau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,36 @@
//! Millau chain specification for CLI.

use crate::cli::{encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use bp_runtime::{ChainId, EncodedOrDecodedCall, RIALTO_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID};
use bridge_runtime_common::CustomNetworkId;
use relay_millau_client::Millau;
use relay_substrate_client::SimpleRuntimeVersion;
use xcm_executor::traits::ExportXcm;

impl CliEncodeMessage for Millau {
fn encode_wire_message(
target: ChainId,
at_target_xcm: xcm::v3::Xcm<()>,
) -> anyhow::Result<Vec<u8>> {
let target = match target {
RIALTO_CHAIN_ID => CustomNetworkId::Rialto.as_network_id(),
RIALTO_PARACHAIN_CHAIN_ID => CustomNetworkId::RialtoParachain.as_network_id(),
_ => return Err(anyhow::format_err!("Unsupported target chain: {:?}", target)),
};

Ok(millau_runtime::xcm_config::ToRialtoOrRialtoParachainSwitchExporter::validate(
target,
0,
&mut Some(Self::dummy_universal_source()?),
&mut Some(target.into()),
&mut Some(at_target_xcm),
)
.map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))?
.0
.1
.0)
}

fn encode_execute_xcm(
message: xcm::VersionedXcm<Self::Call>,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Expand Down
25 changes: 24 additions & 1 deletion relays/bin-substrate/src/chains/rialto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,34 @@
//! Rialto chain specification for CLI.

use crate::cli::{encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use bp_runtime::{ChainId, EncodedOrDecodedCall, MILLAU_CHAIN_ID};
use bridge_runtime_common::CustomNetworkId;
use relay_rialto_client::Rialto;
use relay_substrate_client::SimpleRuntimeVersion;
use xcm_executor::traits::ExportXcm;

impl CliEncodeMessage for Rialto {
fn encode_wire_message(
target: ChainId,
at_target_xcm: xcm::v3::Xcm<()>,
) -> anyhow::Result<Vec<u8>> {
let target = match target {
MILLAU_CHAIN_ID => CustomNetworkId::Millau.as_network_id(),
_ => return Err(anyhow::format_err!("Unsupported target chian: {:?}", target)),
};

Ok(rialto_runtime::millau_messages::ToMillauBlobExporter::validate(
target,
0,
&mut Some(Self::dummy_universal_source()?),
&mut Some(target.into()),
&mut Some(at_target_xcm),
)
.map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))?
.0
.0)
}

fn encode_execute_xcm(
message: xcm::VersionedXcm<Self::Call>,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Expand Down
25 changes: 24 additions & 1 deletion relays/bin-substrate/src/chains/rialto_parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,34 @@
//! Rialto parachain specification for CLI.

use crate::cli::{encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use bp_runtime::{ChainId, EncodedOrDecodedCall, MILLAU_CHAIN_ID};
use bridge_runtime_common::CustomNetworkId;
use relay_rialto_parachain_client::RialtoParachain;
use relay_substrate_client::SimpleRuntimeVersion;
use xcm_executor::traits::ExportXcm;

impl CliEncodeMessage for RialtoParachain {
fn encode_wire_message(
target: ChainId,
at_target_xcm: xcm::v3::Xcm<()>,
) -> anyhow::Result<Vec<u8>> {
let target = match target {
MILLAU_CHAIN_ID => CustomNetworkId::Millau.as_network_id(),
_ => return Err(anyhow::format_err!("Unsupported target chain: {:?}", target)),
};

Ok(rialto_parachain_runtime::millau_messages::ToMillauBlobExporter::validate(
target,
0,
&mut Some(Self::dummy_universal_source()?),
&mut Some(target.into()),
&mut Some(at_target_xcm),
)
.map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))?
.0
.0)
}

fn encode_execute_xcm(
message: xcm::VersionedXcm<Self::Call>,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Expand Down
97 changes: 69 additions & 28 deletions relays/bin-substrate/src/cli/encode_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.

use crate::cli::{ExplicitOrMaximal, HexBytes};
use bp_runtime::EncodedOrDecodedCall;
use bp_runtime::{ChainId, EncodedOrDecodedCall};
use bridge_runtime_common::CustomNetworkId;
use codec::Encode;
use frame_support::weights::Weight;
use relay_substrate_client::Chain;
use structopt::StructOpt;
use xcm::latest::prelude::*;

/// All possible messages that may be delivered to generic Substrate chain.
///
Expand All @@ -43,6 +45,31 @@ pub enum Message {
pub type RawMessage = Vec<u8>;

pub trait CliEncodeMessage: Chain {
/// Returns dummy `AccountId32` universal source given this network id.
fn dummy_universal_source() -> anyhow::Result<xcm::v3::Junctions> {
use xcm::v3::prelude::*;

let this_network = CustomNetworkId::try_from(Self::ID)
.map(|n| n.as_network_id())
.map_err(|_| anyhow::format_err!("Unsupported chain: {:?}", Self::ID))?;
let this_location: InteriorMultiLocation = this_network.into();

let origin = MultiLocation {
parents: 0,
interior: X1(AccountId32 { network: Some(this_network), id: [0u8; 32] }),
};
let universal_source = this_location
.within_global(origin)
.map_err(|e| anyhow::format_err!("Invalid location: {:?}", e))?;

Ok(universal_source)
}
/// Returns XCM blob that is passed to the `send_message` function of the messages pallet
/// and then is sent over the wire.
fn encode_wire_message(
target: ChainId,
at_target_xcm: xcm::v3::Xcm<()>,
) -> anyhow::Result<Vec<u8>>;
/// Encode an `execute` XCM call of the XCM pallet.
fn encode_execute_xcm(
message: xcm::VersionedXcm<Self::Call>,
Expand All @@ -56,41 +83,42 @@ pub trait CliEncodeMessage: Chain {
}

/// Encode message payload passed through CLI flags.
pub(crate) fn encode_message<Source: Chain, Target: Chain>(
pub(crate) fn encode_message<Source: CliEncodeMessage, Target: Chain>(
message: &Message,
) -> anyhow::Result<RawMessage> {
Ok(match message {
Message::Raw { ref data } => data.0.clone(),
Message::Sized { ref size } => {
let expected_xcm_size = match *size {
let destination = CustomNetworkId::try_from(Target::ID)
.map(|n| n.as_network_id())
.map_err(|_| anyhow::format_err!("Unsupported target chain: {:?}", Target::ID))?;
let expected_size = match *size {
ExplicitOrMaximal::Explicit(size) => size,
ExplicitOrMaximal::Maximal => compute_maximal_message_size(
Source::max_extrinsic_size(),
Target::max_extrinsic_size(),
),
};

// there's no way to craft XCM of the given size - we'll be using `ExpectPallet`
// instruction, which has byte vector inside
let mut current_vec_size = expected_xcm_size;
let xcm = loop {
let xcm = xcm::VersionedXcm::<()>::V3(
vec![xcm::v3::Instruction::ExpectPallet {
index: 0,
name: vec![42; current_vec_size as usize],
module_name: vec![],
crate_major: 0,
min_crate_minor: 0,
}]
.into(),
);
if xcm.encode().len() <= expected_xcm_size as usize {
break xcm
}

current_vec_size -= 1;
};
xcm.encode()
} as usize;

let at_target_xcm = vec![xcm::v3::Instruction::ClearOrigin; expected_size].into();
let at_target_xcm_size =
Source::encode_wire_message(Target::ID, at_target_xcm)?.encoded_size();
let at_target_xcm_overhead = at_target_xcm_size.saturating_sub(expected_size);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get the overhead just by calling encode_wire_message(Target::ID, vec![]) ? Or by sending a None as the xcm message to validate() (inside encode_wire_message()) ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll probably make our messages 1 or 2 bytes above the maximal allowed size because Compact<u32> (length of XCM instructions vector) is larger for larger vectors => this message will be rejected. Current approach may made messages 1-byte smaller in edge cases, but it is still fine. And yeah - it seems a bit hacky, but I haven't found any better way

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree that this version is the safest.

let at_target_xcm = vec![
xcm::v3::Instruction::ClearOrigin;
expected_size.saturating_sub(at_target_xcm_overhead)
]
.into();

xcm::VersionedXcm::<()>::V3(
vec![ExportMessage {
network: destination,
destination: destination.into(),
xcm: at_target_xcm,
}]
.into(),
)
.encode()
},
})
}
Expand Down Expand Up @@ -123,13 +151,21 @@ mod tests {
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;

fn approximate_message_size<Source: CliEncodeMessage>(xcm_msg_len: usize) -> usize {
xcm_msg_len + Source::dummy_universal_source().unwrap().encoded_size()
}

#[test]
fn encode_explicit_size_message_works() {
let msg = encode_message::<Rialto, Millau>(&Message::Sized {
size: ExplicitOrMaximal::Explicit(100),
})
.unwrap();
assert_eq!(msg.len(), 100);
// since it isn't the returned XCM what is sent over the wire, we can only check if
// it is close to what we need
assert!(
(1f64 - (approximate_message_size::<Rialto>(msg.len()) as f64) / 100_f64).abs() < 0.1
);
// check that it decodes to valid xcm
let _ = decode_xcm::<()>(msg).unwrap();
}
Expand All @@ -144,7 +180,12 @@ mod tests {
let msg =
encode_message::<Rialto, Millau>(&Message::Sized { size: ExplicitOrMaximal::Maximal })
.unwrap();
assert_eq!(msg.len(), maximal_size as usize);
// since it isn't the returned XCM what is sent over the wire, we can only check if
// it is close to what we need
assert!(
(1f64 - approximate_message_size::<Rialto>(msg.len()) as f64 / maximal_size as f64)
.abs() < 0.1
);
// check that it decodes to valid xcm
let _ = decode_xcm::<()>(msg).unwrap();
}
Expand Down