lib.rs 40.6 KiB
Newer Older
Gavin Wood's avatar
Gavin Wood committed
			},
			ExpectTransactStatus(transact_status) => {
				ensure!(self.transact_status == transact_status, XcmError::ExpectationFalse);
				Ok(())
			},
Gavin Wood's avatar
Gavin Wood committed
			QueryPallet { module_name, response_info } => {
				let pallets = Config::PalletInstancesInfo::infos()
					.into_iter()
					.filter(|x| x.module_name.as_bytes() == &module_name[..])
					.map(|x| {
						PalletInfo::new(
							x.index as u32,
							x.name.as_bytes().into(),
							x.module_name.as_bytes().into(),
							x.crate_version.major as u32,
							x.crate_version.minor as u32,
							x.crate_version.patch as u32,
						)
					})
					.collect::<Result<Vec<_>, XcmError>>()?;
				let QueryResponseInfo { destination, query_id, max_weight } = response_info;
				let response =
					Response::PalletsInfo(pallets.try_into().map_err(|_| XcmError::Overflow)?);
Gavin Wood's avatar
Gavin Wood committed
				let querier = Self::to_querier(self.cloned_origin(), &destination)?;
				let instruction = QueryResponse { query_id, response, max_weight, querier };
				let message = Xcm(vec![instruction]);
				self.send(destination, message, FeeReason::QueryPallet)?;
				Ok(())
			},
			ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
				let pallet = Config::PalletInstancesInfo::infos()
					.into_iter()
					.find(|x| x.index == index as usize)
					.ok_or(XcmError::PalletNotFound)?;
				ensure!(pallet.name.as_bytes() == &name[..], XcmError::NameMismatch);
				ensure!(pallet.module_name.as_bytes() == &module_name[..], XcmError::NameMismatch);
				let major = pallet.crate_version.major as u32;
				ensure!(major == crate_major, XcmError::VersionIncompatible);
				let minor = pallet.crate_version.minor as u32;
				ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible);
				Ok(())
			},
			ReportTransactStatus(response_info) => {
				self.respond(
					self.cloned_origin(),
					Response::DispatchResult(self.transact_status.clone()),
					response_info,
					FeeReason::Report,
				)?;
				Ok(())
			},
			ClearTransactStatus => {
				self.transact_status = Default::default();
				Ok(())
			},
			UniversalOrigin(new_global) => {
				let universal_location = Config::UniversalLocation::get();
				ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation);
Francisco Aguirre's avatar
Francisco Aguirre committed
				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
				let origin_xform = (origin, new_global);
				let ok = Config::UniversalAliases::contains(&origin_xform);
				ensure!(ok, XcmError::InvalidLocation);
				let (_, new_global) = origin_xform;
Francisco Aguirre's avatar
Francisco Aguirre committed
				let new_origin = Junctions::from([new_global]).relative_to(&universal_location);
Gavin Wood's avatar
Gavin Wood committed
				self.context.origin = Some(new_origin);
				Ok(())
			},
			ExportMessage { network, destination, xcm } => {
				// The actual message sent to the bridge for forwarding is prepended with
				// `UniversalOrigin` and `DescendOrigin` in order to ensure that the message is
				// executed with this Origin.
Gavin Wood's avatar
Gavin Wood committed
				//
				// Prepend the desired message with instructions which effectively rewrite the
				// origin.
Gavin Wood's avatar
Gavin Wood committed
				//
				// This only works because the remote chain empowers the bridge
				// to speak for the local network.
Francisco Aguirre's avatar
Francisco Aguirre committed
				let origin = self.context.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone();
Gavin Wood's avatar
Gavin Wood committed
				let universal_source = Config::UniversalLocation::get()
					.within_global(origin)
					.map_err(|()| XcmError::Unanchored)?;
				let hash = (self.origin_ref(), &destination).using_encoded(blake2_128);
				let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
				// Hash identifies the lane on the exporter which we use. We use the pairwise
				// combination of the origin and destination to ensure origin/destination pairs
				// will generally have their own lanes.
Gavin Wood's avatar
Gavin Wood committed
				let (ticket, fee) = validate_export::<Config::MessageExporter>(
					network,
					channel,
					universal_source,
Francisco Aguirre's avatar
Francisco Aguirre committed
					destination.clone(),
Gavin Wood's avatar
Gavin Wood committed
					xcm,
				)?;
				let old_holding = self.holding.clone();
				let result = Config::TransactionalProcessor::process(|| {
					self.take_fee(fee, FeeReason::Export { network, destination })?;
					let _ = Config::MessageExporter::deliver(ticket).defensive_proof(
						"`deliver` called immediately after `validate_export`; \
						`take_fee` does not affect the validity of the ticket; qed",
					);
					Ok(())
				});
				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
					self.holding = old_holding;
				}
				result
Gavin Wood's avatar
Gavin Wood committed
			},
			LockAsset { asset, unlocker } => {
				let old_holding = self.holding.clone();
				let result = Config::TransactionalProcessor::process(|| {
					let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
					let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?;
					let lock_ticket =
						Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?;
					let owner = origin
						.reanchored(&unlocker, &context)
						.map_err(|_| XcmError::ReanchorFailed)?;
					let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]);
					let (ticket, price) = validate_send::<Config::XcmSender>(unlocker, msg)?;
					self.take_fee(price, FeeReason::LockAsset)?;
					lock_ticket.enact()?;
					Config::XcmSender::deliver(ticket)?;
					Ok(())
				});
				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
					self.holding = old_holding;
				}
				result
Gavin Wood's avatar
Gavin Wood committed
			},
			UnlockAsset { asset, target } => {
Francisco Aguirre's avatar
Francisco Aguirre committed
				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
				Config::AssetLocker::prepare_unlock(origin, asset, target)?.enact()?;
				Ok(())
			},
			NoteUnlockable { asset, owner } => {
Francisco Aguirre's avatar
Francisco Aguirre committed
				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
				Config::AssetLocker::note_unlockable(origin, asset, owner)?;
				Ok(())
			},
			RequestUnlock { asset, locker } => {
Francisco Aguirre's avatar
Francisco Aguirre committed
				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
				let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0;
Francisco Aguirre's avatar
Francisco Aguirre committed
				let remote_target = Self::try_reanchor(origin.clone(), &locker)?.0;
				let reduce_ticket = Config::AssetLocker::prepare_reduce_unlockable(
					locker.clone(),
					asset,
					origin.clone(),
				)?;
				let msg =
					Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]);
Gavin Wood's avatar
Gavin Wood committed
				let (ticket, price) = validate_send::<Config::XcmSender>(locker, msg)?;
				let old_holding = self.holding.clone();
				let result = Config::TransactionalProcessor::process(|| {
					self.take_fee(price, FeeReason::RequestUnlock)?;
					reduce_ticket.enact()?;
					Config::XcmSender::deliver(ticket)?;
					Ok(())
				});
				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
					self.holding = old_holding;
				}
				result
Gavin Wood's avatar
Gavin Wood committed
			},
			ExchangeAsset { give, want, maximal } => {
				let old_holding = self.holding.clone();
Gavin Wood's avatar
Gavin Wood committed
				let give = self.holding.saturating_take(give);
				let result = (|| -> Result<(), XcmError> {
					self.ensure_can_subsume_assets(want.len())?;
					let exchange_result = Config::AssetExchanger::exchange_asset(
						self.origin_ref(),
						give,
						&want,
						maximal,
					);
					if let Ok(received) = exchange_result {
						self.holding.subsume_assets(received.into());
						Ok(())
					} else {
						Err(XcmError::NoDeal)
					}
				})();
				if result.is_err() {
					self.holding = old_holding;
Gavin Wood's avatar
Gavin Wood committed
				}
Gavin Wood's avatar
Gavin Wood committed
			},
			SetFeesMode { jit_withdraw } => {
				self.fees_mode = FeesMode { jit_withdraw };
				Ok(())
			},
			SetTopic(topic) => {
				self.context.topic = Some(topic);
				Ok(())
			},
			ClearTopic => {
				self.context.topic = None;
				Ok(())
			},
			AliasOrigin(target) => {
				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
				if Config::Aliasers::contains(origin, &target) {
					self.context.origin = Some(target);
					Ok(())
				} else {
					Err(XcmError::NoPermission)
				}
			},
Gavin Wood's avatar
Gavin Wood committed
			UnpaidExecution { check_origin, .. } => {
				ensure!(
					check_origin.is_none() || self.context.origin == check_origin,
					XcmError::BadOrigin
				);
				Ok(())
			HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented),
			HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented),
			HrmpChannelClosing { .. } => Err(XcmError::Unimplemented),