diff --git a/Cargo.lock b/Cargo.lock index 85f4ab4b2234..486a0b15695f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21342,6 +21342,7 @@ dependencies = [ "hex-literal", "log", "pallet-balances", + "pallet-xcm", "parity-scale-codec", "scale-info", "serde", @@ -21443,6 +21444,7 @@ name = "snowbridge-router-primitives" version = "0.9.0" dependencies = [ "frame-support", + "frame-system", "hex-literal", "log", "parity-scale-codec", diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs new file mode 100644 index 000000000000..803949346634 --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! Helpers for implementing runtime api + +use crate::{Config, Error}; +use snowbridge_core::inbound::Proof; +use snowbridge_router_primitives::inbound::v2::{ConvertMessage, Message}; +use xcm::{ + latest::Xcm, + prelude::{Junction::*, Location, SendError as XcmpSendError, SendXcm}, +}; +pub fn dry_run(message: Message, proof: Proof) -> Result<(Xcm<()>, u128), Error> +where + T: Config, +{ + Ok((Xcm::<()>::new(), 0)) +} diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 080ee1c3a6bd..cf435a3f6ad5 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -22,7 +22,7 @@ //! * [`Call::submit`]: Submit a message for verification and dispatch the final destination //! parachain. #![cfg_attr(not(feature = "std"), no_std)] - +pub mod api; mod envelope; #[cfg(feature = "runtime-benchmarks")] @@ -41,9 +41,11 @@ use envelope::Envelope; use frame_support::PalletError; use frame_system::ensure_signed; use scale_info::TypeInfo; +use snowbridge_core::inbound::Proof; use sp_core::H160; use sp_std::vec; use xcm::{ + latest::Xcm, prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm}, }; @@ -51,8 +53,7 @@ use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, BasicOperatingMode, }; -use snowbridge_router_primitives::inbound::v2::Message as MessageV2; -use snowbridge_router_primitives::inbound::v2::ConvertMessage; +use snowbridge_router_primitives::inbound::v2::{ConvertMessage, Message as MessageV2}; pub use weights::WeightInfo; @@ -89,7 +90,6 @@ pub mod pallet { /// XCM message sender type XcmSender: SendXcm; - /// Address of the Gateway contract #[pallet::constant] type GatewayAddress: Get; @@ -208,7 +208,8 @@ pub mod pallet { let message = MessageV2::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; - let xcm = T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; + let xcm = + T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: // T::RewardLeger::deposit(who, envelope.fee.into())?; @@ -246,4 +247,19 @@ pub mod pallet { Ok(()) } } + + impl Pallet { + pub fn dry_run_message( + message: Message, + proof: Proof, + ) -> Result<(Xcm<()>, u128), Error> { + let xcm = + T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; + let origin_location = Location::new(1, Parachain(1002).into()); + let dry_run_result = + pallet_xcm::dry_run_xcm(origin_location, xcm).map_err(Error::::from)?; + + Ok((xcm, 0)) + } + } } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 6ff547172ee3..a3874598ec9c 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork use super::*; +use crate::{self as inbound_queue}; use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::IdentityFee}; use hex_literal::hex; use snowbridge_beacon_primitives::{ @@ -11,6 +12,7 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; +use snowbridge_router_primitives::inbound::v2::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, @@ -18,8 +20,6 @@ use sp_runtime::{ }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; -use snowbridge_router_primitives::inbound::v2::MessageToXcm; -use crate::{self as inbound_queue}; type Block = frame_system::mocking::MockBlock; @@ -158,10 +158,7 @@ impl inbound_queue::Config for Test { type WeightInfo = (); type GatewayAddress = GatewayAddress; type AssetHubParaId = ConstU32<1000>; - type MessageConverter = MessageToXcm< - EthereumNetwork, - InboundQueuePalletInstance, - >; + type MessageConverter = MessageToXcm; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 475539e6e1a9..68fcf49b53d7 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -10,16 +10,16 @@ use sp_runtime::DispatchError; use crate::{mock::*, Error, Event as InboundQueueEvent}; use codec::DecodeLimit; -use snowbridge_router_primitives::inbound::v2::InboundAsset; +use snowbridge_router_primitives::inbound::v2::{ConvertMessage, InboundAsset}; use sp_core::H256; -use xcm::opaque::latest::{ - prelude::{ClearOrigin, ReceiveTeleportedAsset}, - Asset, AssetId, Assets, +use xcm::{ + opaque::latest::{ + prelude::{ClearOrigin, ReceiveTeleportedAsset}, + Asset, AssetId, Assets, + }, + prelude::{Junction::AccountKey20, *}, + VersionedXcm, MAX_XCM_DECODE_DEPTH, }; -use xcm::VersionedXcm; -use xcm::MAX_XCM_DECODE_DEPTH; -use snowbridge_router_primitives::inbound::v2::ConvertMessage; -use xcm::prelude::{Junction::AccountKey20, *}; #[test] fn test_submit_happy_path() { diff --git a/bridges/snowbridge/primitives/router/Cargo.toml b/bridges/snowbridge/primitives/router/Cargo.toml index 664f2dbf7930..aa4b3177c00b 100644 --- a/bridges/snowbridge/primitives/router/Cargo.toml +++ b/bridges/snowbridge/primitives/router/Cargo.toml @@ -17,6 +17,7 @@ scale-info = { features = ["derive"], workspace = true } log = { workspace = true } frame-support = { workspace = true } +frame-system = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } @@ -37,6 +38,7 @@ default = ["std"] std = [ "codec/std", "frame-support/std", + "frame-system/std", "log/std", "scale-info/std", "snowbridge-core/std", @@ -50,6 +52,7 @@ std = [ ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", "snowbridge-core/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/bridges/snowbridge/primitives/router/src/inbound/dry_run.rs b/bridges/snowbridge/primitives/router/src/inbound/dry_run.rs new file mode 100644 index 000000000000..4b8c4bc87593 --- /dev/null +++ b/bridges/snowbridge/primitives/router/src/inbound/dry_run.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use crate::inbound::v2::{ConvertMessage, Message}; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::{GetDispatchInfo, PostDispatchInfo}, + Parameter, +}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Dispatchable, PhantomData}, + Weight, +}; +use xcm::{ + latest::Xcm, + opaque::latest::{ExecuteXcm, Junction, Junction::Parachain, Location}, +}; +use xcm_builder::InspectMessageQueues; + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum DryRunError { + /// Message cannot be decoded. + InvalidPayload, + /// An API call is unsupported. + Unimplemented, + /// Converting a versioned data structure from one version to another failed. + VersionedConversionFailed, +} + +pub trait DryRunMessage { + fn dry_run_xcm(message: Message) -> Result, DryRunError>; +} + +pub struct MessageToFeeEstimate +where + Runtime: frame_system::Config, + Router: InspectMessageQueues, + RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable::RuntimeOrigin, PostInfo = PostDispatchInfo>, + XcmExecutor: ExecuteXcm<::RuntimeCall>, + MessageConverter: ConvertMessage, +{ + _phantom: PhantomData<(Runtime, Router, RuntimeCall, XcmExecutor, MessageConverter)>, +} + +impl DryRunMessage + for MessageToFeeEstimate +where + Runtime: frame_system::Config, + Router: InspectMessageQueues, + RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable::RuntimeOrigin, PostInfo = PostDispatchInfo>, + XcmExecutor: ExecuteXcm<::RuntimeCall>, + MessageConverter: ConvertMessage, +{ + fn dry_run_xcm(message: Message) -> Result, DryRunError> { + let message_xcm = + MessageConverter::convert(message).map_err(|error| DryRunError::InvalidPayload)?; + let origin_location = Location::new(1, Parachain(1002)); + + let xcm_program = Xcm::::from(message_xcm.clone().try_into().unwrap()); + + let origin_location: Location = origin_location + .try_into() + .map_err(|error| DryRunError::VersionedConversionFailed)?; + let xcm: Xcm = + xcm_program.try_into().map_err(|error| DryRunError::VersionedConversionFailed)?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + frame_system::Pallet::::reset_events(); // To make sure we only record events from current call. + let result = XcmExecutor::prepare_and_execute( + origin_location, + xcm, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = Router::get_messages(); + let events: Vec<::RuntimeEvent> = + frame_system::Pallet::::read_events_no_consensus() + .map(|record| record.event.clone()) + .collect(); + Ok(vec![].into()) + } +} diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 37890e878603..1c8e68abf1be 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork // SPDX-FileCopyrightText: 2021-2022 Parity Technologies (UK) Ltd. +pub mod dry_run; pub mod v1; pub mod v2; use codec::Encode; diff --git a/bridges/snowbridge/primitives/router/src/inbound/v2.rs b/bridges/snowbridge/primitives/router/src/inbound/v2.rs index a849c3891bfa..9cc273b01392 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v2.rs @@ -83,16 +83,15 @@ where InboundQueuePalletInstance: Get, { fn convert(message: Message) -> Result, ConvertMessageError> { - let mut message_xcm : Xcm<()> = Xcm::new(); - if message.xcm.len() > 0{ + let mut message_xcm: Xcm<()> = Xcm::new(); + if message.xcm.len() > 0 { // Decode xcm let versioned_xcm = VersionedXcm::<()>::decode_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut message.xcm.as_ref(), ) - .map_err(|_| ConvertMessageError::InvalidVersionedXCM)?; - message_xcm = - versioned_xcm.try_into().map_err(|_| ConvertMessageError::InvalidXCM)?; + .map_err(|_| ConvertMessageError::InvalidVersionedXCM)?; + message_xcm = versioned_xcm.try_into().map_err(|_| ConvertMessageError::InvalidXCM)?; } log::debug!(target: LOG_TARGET,"xcm decoded as {:?}", message_xcm);