snowbridge_outbound_queue_primitives/v2/converter/
mod.rs1#[cfg(test)]
6mod tests;
7
8pub mod convert;
9pub use convert::XcmConverter;
10
11use super::message::SendMessage;
12use codec::{Decode, Encode};
13use frame_support::{
14 ensure,
15 traits::{Contains, Get, ProcessMessageError},
16};
17use snowbridge_core::{ParaId, TokenId};
18use sp_runtime::traits::MaybeConvert;
19use sp_std::{marker::PhantomData, ops::ControlFlow, prelude::*};
20use xcm::prelude::*;
21use xcm_builder::{CreateMatcher, ExporterFor, MatchXcm};
22use xcm_executor::traits::ExportXcm;
23
24pub const TARGET: &'static str = "xcm::ethereum_blob_exporter::v2";
25
26pub struct EthereumBlobExporter<
29 UniversalLocation,
30 EthereumNetwork,
31 OutboundQueue,
32 ConvertAssetId,
33 AssetHubParaId,
34>(
35 PhantomData<(
36 UniversalLocation,
37 EthereumNetwork,
38 OutboundQueue,
39 ConvertAssetId,
40 AssetHubParaId,
41 )>,
42);
43
44impl<UniversalLocation, EthereumNetwork, OutboundQueue, ConvertAssetId, AssetHubParaId> ExportXcm
45 for EthereumBlobExporter<
46 UniversalLocation,
47 EthereumNetwork,
48 OutboundQueue,
49 ConvertAssetId,
50 AssetHubParaId,
51 >
52where
53 UniversalLocation: Get<InteriorLocation>,
54 EthereumNetwork: Get<NetworkId>,
55 OutboundQueue: SendMessage,
56 ConvertAssetId: MaybeConvert<TokenId, Location>,
57 AssetHubParaId: Get<ParaId>,
58{
59 type Ticket = (Vec<u8>, XcmHash);
60
61 fn validate(
62 network: NetworkId,
63 _channel: u32,
64 universal_source: &mut Option<InteriorLocation>,
65 destination: &mut Option<InteriorLocation>,
66 message: &mut Option<Xcm<()>>,
67 ) -> SendResult<Self::Ticket> {
68 log::debug!(target: TARGET, "message route through bridge {message:?}.");
69
70 let expected_network = EthereumNetwork::get();
71 let universal_location = UniversalLocation::get();
72
73 if network != expected_network {
74 log::trace!(target: TARGET, "skipped due to unmatched bridge network {network:?}.");
75 return Err(SendError::NotApplicable)
76 }
77
78 let dest = destination.clone().ok_or(SendError::MissingArgument)?;
80 if dest != Here {
81 log::trace!(target: TARGET, "skipped due to unmatched remote destination {dest:?}.");
82 return Err(SendError::NotApplicable)
83 }
84
85 let (local_net, local_sub) = universal_source.clone()
87 .ok_or_else(|| {
88 log::error!(target: TARGET, "universal source not provided.");
89 SendError::MissingArgument
90 })?
91 .split_global()
92 .map_err(|()| {
93 log::error!(target: TARGET, "could not get global consensus from universal source '{universal_source:?}'.");
94 SendError::NotApplicable
95 })?;
96
97 if Ok(local_net) != universal_location.global_consensus() {
98 log::trace!(target: TARGET, "skipped due to unmatched relay network {local_net:?}.");
99 return Err(SendError::NotApplicable)
100 }
101
102 let para_id = match local_sub.as_slice() {
103 [Parachain(para_id)] => *para_id,
104 _ => {
105 log::error!(target: TARGET, "could not get parachain id from universal source '{local_sub:?}'.");
106 return Err(SendError::NotApplicable)
107 },
108 };
109
110 if ParaId::from(para_id) != AssetHubParaId::get() {
111 log::error!(target: TARGET, "is not from asset hub '{para_id:?}'.");
112 return Err(SendError::NotApplicable)
113 }
114
115 let message = message.clone().ok_or_else(|| {
116 log::error!(target: TARGET, "xcm message not provided.");
117 SendError::MissingArgument
118 })?;
119
120 let mut instructions = message.clone().0;
126 let result = instructions.matcher().match_next_inst_while(
127 |_| true,
128 |inst| {
129 return match inst {
130 AliasOrigin(..) => Err(ProcessMessageError::Yield),
131 _ => Ok(ControlFlow::Continue(())),
132 }
133 },
134 );
135 ensure!(result.is_err(), SendError::NotApplicable);
136
137 let mut converter = XcmConverter::<ConvertAssetId, ()>::new(&message, expected_network);
138 let message = converter.convert().map_err(|err| {
139 log::error!(target: TARGET, "unroutable due to pattern matching error '{err:?}'.");
140 SendError::Unroutable
141 })?;
142
143 let ticket = OutboundQueue::validate(&message).map_err(|err| {
145 log::error!(target: TARGET, "OutboundQueue validation of message failed. {err:?}");
146 SendError::Unroutable
147 })?;
148
149 Ok(((ticket.encode(), XcmHash::from(message.id)), Assets::default()))
150 }
151
152 fn deliver(blob: (Vec<u8>, XcmHash)) -> Result<XcmHash, SendError> {
153 let ticket: OutboundQueue::Ticket = OutboundQueue::Ticket::decode(&mut blob.0.as_ref())
154 .map_err(|_| {
155 log::trace!(target: TARGET, "undeliverable due to decoding error");
156 SendError::NotApplicable
157 })?;
158
159 let message_id = OutboundQueue::deliver(ticket).map_err(|_| {
160 log::error!(target: TARGET, "OutboundQueue submit of message failed");
161 SendError::Transport("other transport error")
162 })?;
163
164 log::info!(target: TARGET, "message delivered {message_id:#?}.");
165 Ok(message_id.into())
166 }
167}
168
169pub struct XcmFilterExporter<T, M>(core::marker::PhantomData<(T, M)>);
173impl<T: ExporterFor, M: Contains<Xcm<()>>> ExporterFor for XcmFilterExporter<T, M> {
174 fn exporter_for(
175 network: &NetworkId,
176 remote_location: &InteriorLocation,
177 xcm: &Xcm<()>,
178 ) -> Option<(Location, Option<Asset>)> {
179 if !M::contains(xcm) {
181 return None
182 }
183 T::exporter_for(network, remote_location, xcm)
185 }
186}
187
188pub struct XcmForSnowbridgeV2;
190impl Contains<Xcm<()>> for XcmForSnowbridgeV2 {
191 fn contains(xcm: &Xcm<()>) -> bool {
192 let mut instructions = xcm.clone().0;
193 let result = instructions.matcher().match_next_inst_while(
194 |_| true,
195 |inst| {
196 return match inst {
197 AliasOrigin(..) => Err(ProcessMessageError::Yield),
198 _ => Ok(ControlFlow::Continue(())),
199 }
200 },
201 );
202 result.is_err()
203 }
204}