referrerpolicy=no-referrer-when-downgrade

staging_xcm_builder/
routing.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Various implementations for `SendXcm`.
18
19use alloc::vec::Vec;
20use codec::Encode;
21use core::{marker::PhantomData, result::Result};
22use frame_system::unique;
23use xcm::prelude::*;
24use xcm_executor::{traits::FeeReason, FeesMode};
25
26/// Wrapper router which, if the message does not already end with a `SetTopic` instruction,
27/// appends one to the message filled with a universally unique ID. This ID is returned from a
28/// successful `deliver`.
29///
30/// If the message does already end with a `SetTopic` instruction, then it is the responsibility
31/// of the code author to ensure that the ID supplied to `SetTopic` is universally unique. Due to
32/// this property, consumers of the topic ID must be aware that a user-supplied ID may not be
33/// unique.
34///
35/// This is designed to be at the top-level of any routers, since it will always mutate the
36/// passed `message` reference into a `None`. Don't try to combine it within a tuple except as the
37/// last element.
38pub struct WithUniqueTopic<Inner>(PhantomData<Inner>);
39impl<Inner: SendXcm> SendXcm for WithUniqueTopic<Inner> {
40	type Ticket = (Inner::Ticket, [u8; 32]);
41
42	fn validate(
43		destination: &mut Option<Location>,
44		message: &mut Option<Xcm<()>>,
45	) -> SendResult<Self::Ticket> {
46		let mut message = message.take().ok_or(SendError::MissingArgument)?;
47		let unique_id = if let Some(SetTopic(id)) = message.last() {
48			*id
49		} else {
50			let unique_id = unique(&message);
51			message.0.push(SetTopic(unique_id));
52			unique_id
53		};
54		let (ticket, assets) = Inner::validate(destination, &mut Some(message))?;
55		Ok(((ticket, unique_id), assets))
56	}
57
58	fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
59		let (ticket, unique_id) = ticket;
60		Inner::deliver(ticket)?;
61		Ok(unique_id)
62	}
63
64	#[cfg(feature = "runtime-benchmarks")]
65	fn ensure_successful_delivery(location: Option<Location>) {
66		Inner::ensure_successful_delivery(location);
67	}
68}
69impl<Inner: InspectMessageQueues> InspectMessageQueues for WithUniqueTopic<Inner> {
70	fn clear_messages() {
71		Inner::clear_messages()
72	}
73
74	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
75		Inner::get_messages()
76	}
77}
78
79pub trait SourceTopic {
80	fn source_topic(entropy: impl Encode) -> XcmHash;
81}
82
83impl SourceTopic for () {
84	fn source_topic(_: impl Encode) -> XcmHash {
85		[0u8; 32]
86	}
87}
88
89/// Wrapper router which, if the message does not already end with a `SetTopic` instruction,
90/// prepends one to the message filled with an ID from `TopicSource`. This ID is returned from a
91/// successful `deliver`.
92///
93/// This is designed to be at the top-level of any routers, since it will always mutate the
94/// passed `message` reference into a `None`. Don't try to combine it within a tuple except as the
95/// last element.
96pub struct WithTopicSource<Inner, TopicSource>(PhantomData<(Inner, TopicSource)>);
97impl<Inner: SendXcm, TopicSource: SourceTopic> SendXcm for WithTopicSource<Inner, TopicSource> {
98	type Ticket = (Inner::Ticket, [u8; 32]);
99
100	fn validate(
101		destination: &mut Option<Location>,
102		message: &mut Option<Xcm<()>>,
103	) -> SendResult<Self::Ticket> {
104		let mut message = message.take().ok_or(SendError::MissingArgument)?;
105		let unique_id = if let Some(SetTopic(id)) = message.last() {
106			*id
107		} else {
108			let unique_id = TopicSource::source_topic(&message);
109			message.0.push(SetTopic(unique_id));
110			unique_id
111		};
112		let (ticket, assets) = Inner::validate(destination, &mut Some(message.clone())).map_err(|e| {
113			tracing::debug!(target: "xcm::validate::WithTopicSource", ?destination, ?message, error = ?e, "Failed to validate");
114			SendError::NotApplicable
115		})?;
116		Ok(((ticket, unique_id), assets))
117	}
118
119	fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
120		let (ticket, unique_id) = ticket;
121		Inner::deliver(ticket)?;
122		Ok(unique_id)
123	}
124
125	#[cfg(feature = "runtime-benchmarks")]
126	fn ensure_successful_delivery(location: Option<Location>) {
127		Inner::ensure_successful_delivery(location);
128	}
129}
130
131/// Trait for a type which ensures all requirements for successful delivery with XCM transport
132/// layers.
133pub trait EnsureDelivery {
134	/// Prepare all requirements for successful `XcmSender: SendXcm` passing (accounts, balances,
135	/// channels ...). Returns:
136	/// - possible `FeesMode` which is expected to be set to executor
137	/// - possible `Assets` which are expected to be subsume to the Holding Register
138	fn ensure_successful_delivery(
139		origin_ref: &Location,
140		dest: &Location,
141		fee_reason: FeeReason,
142	) -> (Option<FeesMode>, Option<Assets>);
143}
144
145/// Tuple implementation for `EnsureDelivery`.
146#[impl_trait_for_tuples::impl_for_tuples(30)]
147impl EnsureDelivery for Tuple {
148	fn ensure_successful_delivery(
149		origin_ref: &Location,
150		dest: &Location,
151		fee_reason: FeeReason,
152	) -> (Option<FeesMode>, Option<Assets>) {
153		for_tuples!( #(
154			// If the implementation returns something, we're done; if not, let others try.
155			match Tuple::ensure_successful_delivery(origin_ref, dest, fee_reason.clone()) {
156				r @ (Some(_), Some(_)) | r @ (Some(_), None) | r @ (None, Some(_)) => return r,
157				(None, None) => (),
158			}
159		)* );
160		// doing nothing
161		(None, None)
162	}
163}
164
165/// Inspects messages in queues.
166/// Meant to be used in runtime APIs, not in runtimes.
167pub trait InspectMessageQueues {
168	/// Clear the queues at the beginning of Runtime API call, so that subsequent
169	/// `Self::get_messages()` will return only messages generated by said Runtime API.
170	fn clear_messages();
171	/// Get queued messages and their destinations.
172	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>;
173}
174
175#[impl_trait_for_tuples::impl_for_tuples(30)]
176impl InspectMessageQueues for Tuple {
177	fn clear_messages() {
178		for_tuples!( #(
179			Tuple::clear_messages();
180		)* );
181	}
182
183	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
184		let mut messages = Vec::new();
185
186		for_tuples!( #(
187			messages.append(&mut Tuple::get_messages());
188		)* );
189
190		messages
191	}
192}
193
194/// A wrapper router that attempts to *encode* and *decode* passed XCM `message` to ensure that the
195/// receiving side will be able to decode, at least with the same XCM version.
196///
197/// This is designed to be at the top-level of any routers which do the real delivery. While other
198/// routers can manipulate the `message`, we cannot access the final XCM due to the generic
199/// `Inner::Ticket`. Therefore, this router aims to validate at least the passed `message`.
200///
201/// NOTE: For use in mock runtimes which don't have the DMP/UMP/HRMP XCM validations.
202pub struct EnsureDecodableXcm<Inner>(core::marker::PhantomData<Inner>);
203impl<Inner: SendXcm> SendXcm for EnsureDecodableXcm<Inner> {
204	type Ticket = Inner::Ticket;
205
206	fn validate(
207		destination: &mut Option<Location>,
208		message: &mut Option<Xcm<()>>,
209	) -> SendResult<Self::Ticket> {
210		if let Some(msg) = message {
211			let versioned_xcm = VersionedXcm::<()>::from(msg.clone());
212			if versioned_xcm.check_is_decodable().is_err() {
213				tracing::debug!(
214					target: "xcm::validate_xcm_nesting",
215					?versioned_xcm,
216					?msg,
217					"EnsureDecodableXcm `validate_xcm_nesting` failed"
218				);
219				return Err(SendError::Transport("EnsureDecodableXcm validate_xcm_nesting error"))
220			}
221		}
222		Inner::validate(destination, message)
223	}
224
225	fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
226		Inner::deliver(ticket)
227	}
228
229	#[cfg(feature = "runtime-benchmarks")]
230	fn ensure_successful_delivery(location: Option<Location>) {
231		Inner::ensure_successful_delivery(location);
232	}
233}