From aac5b1e364d29c33fb26d4422c29a83966729be2 Mon Sep 17 00:00:00 2001
From: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Date: Fri, 26 May 2023 16:41:10 -0300
Subject: [PATCH] XCM: Fix issue with RequestUnlock (#7278)

* XCM: Fix issue with RequestUnlock

* Leave API changes for v4

* Fix clippy errors

* Fix tests

---------

Co-authored-by: parity-processbot <>
---
 polkadot/xcm/xcm-builder/src/tests/locking.rs |  8 +++--
 polkadot/xcm/xcm-executor/src/lib.rs          | 15 +++++++++-
 polkadot/xcm/xcm-simulator/example/src/lib.rs | 26 +++++++++++++++--
 .../xcm-simulator/example/src/parachain.rs    | 29 +++++++++++++++----
 4 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/polkadot/xcm/xcm-builder/src/tests/locking.rs b/polkadot/xcm/xcm-builder/src/tests/locking.rs
index 7c408c999b5..f4ef618ac0e 100644
--- a/polkadot/xcm/xcm-builder/src/tests/locking.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/locking.rs
@@ -136,6 +136,8 @@ fn remote_unlock_roundtrip_should_work() {
 	set_send_price((Parent, 10u128));
 
 	// We have been told by Parachain #1 that Account #3 has locked funds which we can unlock.
+	// Previously, we must have sent a LockAsset instruction to Parachain #1.
+	// This caused Parachain #1 to send us the NoteUnlockable instruction.
 	let message =
 		Xcm(vec![NoteUnlockable { asset: (Parent, 100u128).into(), owner: (3u64,).into() }]);
 	let hash = fake_message_hash(&message);
@@ -169,8 +171,10 @@ fn remote_unlock_roundtrip_should_work() {
 	assert_eq!(r, Outcome::Complete(Weight::from_parts(40, 40)));
 	assert_eq!(asset_list((3u64,)), vec![(Parent, 990u128).into()]);
 
-	let expected_msg =
-		Xcm::<()>(vec![UnlockAsset { target: (3u64,).into(), asset: (Parent, 100u128).into() }]);
+	let expected_msg = Xcm::<()>(vec![UnlockAsset {
+		target: (Parent, Parachain(42), 3u64).into(),
+		asset: (Parent, 100u128).into(),
+	}]);
 	let expected_hash = fake_message_hash(&expected_msg);
 	assert_eq!(sent_xcm(), vec![((Parent, Parachain(1)).into(), expected_msg, expected_hash)]);
 	assert_eq!(
diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs
index ce9d3d4644e..16f53fd6503 100644
--- a/polkadot/xcm/xcm-executor/src/lib.rs
+++ b/polkadot/xcm/xcm-executor/src/lib.rs
@@ -876,9 +876,11 @@ impl<Config: config::Config> XcmExecutor<Config> {
 			RequestUnlock { asset, locker } => {
 				let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?;
 				let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0;
+				let remote_target = Self::try_reanchor_multilocation(origin, &locker)?.0;
 				let reduce_ticket =
 					Config::AssetLocker::prepare_reduce_unlockable(locker, asset, origin)?;
-				let msg = Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: origin }]);
+				let msg =
+					Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]);
 				let (ticket, price) = validate_send::<Config::XcmSender>(locker, msg)?;
 				self.take_fee(price, FeeReason::RequestUnlock)?;
 				reduce_ticket.enact()?;
@@ -990,6 +992,17 @@ impl<Config: config::Config> XcmExecutor<Config> {
 		Ok((asset, reanchor_context))
 	}
 
+	fn try_reanchor_multilocation(
+		location: MultiLocation,
+		destination: &MultiLocation,
+	) -> Result<(MultiLocation, InteriorMultiLocation), XcmError> {
+		let reanchor_context = Config::UniversalLocation::get();
+		let location = location
+			.reanchored(&destination, reanchor_context)
+			.map_err(|_| XcmError::ReanchorFailed)?;
+		Ok((location, reanchor_context))
+	}
+
 	/// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`.
 	fn reanchored(
 		mut assets: Assets,
diff --git a/polkadot/xcm/xcm-simulator/example/src/lib.rs b/polkadot/xcm/xcm-simulator/example/src/lib.rs
index 33a5b2c70a9..bd5ebb0b472 100644
--- a/polkadot/xcm/xcm-simulator/example/src/lib.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/lib.rs
@@ -272,7 +272,7 @@ mod tests {
 	}
 
 	#[test]
-	fn remote_locking() {
+	fn remote_locking_and_unlocking() {
 		MockNet::reset();
 
 		let locked_amount = 100;
@@ -280,7 +280,7 @@ mod tests {
 		ParaB::execute_with(|| {
 			let message = Xcm(vec![LockAsset {
 				asset: (Here, locked_amount).into(),
-				unlocker: (Parachain(1),).into(),
+				unlocker: Parachain(1).into(),
 			}]);
 			assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone()));
 		});
@@ -306,6 +306,28 @@ mod tests {
 				}])]
 			);
 		});
+
+		ParaB::execute_with(|| {
+			// Request unlocking part of the funds on the relay chain
+			let message = Xcm(vec![RequestUnlock {
+				asset: (Parent, locked_amount - 50).into(),
+				locker: Parent.into(),
+			}]);
+			assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(1)), message));
+		});
+
+		Relay::execute_with(|| {
+			use pallet_balances::{BalanceLock, Reasons};
+			// Lock is reduced
+			assert_eq!(
+				relay_chain::Balances::locks(&child_account_id(2)),
+				vec![BalanceLock {
+					id: *b"py/xcmlk",
+					amount: locked_amount - 50,
+					reasons: Reasons::All
+				}]
+			);
+		});
 	}
 
 	/// Scenario:
diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs
index cea5a93ec0a..39a2e27470b 100644
--- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs
@@ -17,9 +17,10 @@
 //! Parachain runtime mock.
 
 use codec::{Decode, Encode};
+use core::marker::PhantomData;
 use frame_support::{
 	construct_runtime, parameter_types,
-	traits::{EnsureOrigin, EnsureOriginWithArg, Everything, EverythingBut, Nothing},
+	traits::{ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, EverythingBut, Nothing},
 	weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
 };
 
@@ -27,7 +28,7 @@ use frame_system::EnsureRoot;
 use sp_core::{ConstU32, H256};
 use sp_runtime::{
 	testing::Header,
-	traits::{Hash, IdentityLookup},
+	traits::{Get, Hash, IdentityLookup},
 	AccountId32,
 };
 use sp_std::prelude::*;
@@ -238,7 +239,7 @@ impl Config for XcmConfig {
 	type Trader = FixedRateOfFungible<KsmPerSecondPerByte, ()>;
 	type ResponseHandler = ();
 	type AssetTrap = ();
-	type AssetLocker = ();
+	type AssetLocker = PolkadotXcm;
 	type AssetExchanger = ();
 	type AssetClaims = ();
 	type SubscriptionService = ();
@@ -325,7 +326,7 @@ pub mod mock_msg_queue {
 				Ok(xcm) => {
 					let location = (Parent, Parachain(sender.into()));
 					match T::XcmExecutor::execute_xcm(location, xcm, message_hash, max_weight) {
-						Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)),
+						Outcome::Error(e) => (Err(e), Event::Fail(Some(hash), e)),
 						Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))),
 						// As far as the caller is concerned, this was dispatched without error, so
 						// we just report the weight used.
@@ -349,7 +350,7 @@ pub mod mock_msg_queue {
 				let _ = XcmpMessageFormat::decode(&mut data_ref)
 					.expect("Simulator encodes with versioned xcm format; qed");
 
-				let mut remaining_fragments = &data_ref[..];
+				let mut remaining_fragments = data_ref;
 				while !remaining_fragments.is_empty() {
 					if let Ok(xcm) =
 						VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments)
@@ -403,6 +404,22 @@ parameter_types! {
 	pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
 }
 
+pub struct TrustedLockerCase<T>(PhantomData<T>);
+impl<T: Get<(MultiLocation, MultiAssetFilter)>> ContainsPair<MultiLocation, MultiAsset>
+	for TrustedLockerCase<T>
+{
+	fn contains(origin: &MultiLocation, asset: &MultiAsset) -> bool {
+		let (o, a) = T::get();
+		a.matches(asset) && &o == origin
+	}
+}
+
+parameter_types! {
+	pub RelayTokenForRelay: (MultiLocation, MultiAssetFilter) = (Parent.into(), Wild(AllOf { id: Concrete(Parent.into()), fun: WildFungible }));
+}
+
+pub type TrustedLockers = TrustedLockerCase<RelayTokenForRelay>;
+
 impl pallet_xcm::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -420,7 +437,7 @@ impl pallet_xcm::Config for Runtime {
 	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
 	type Currency = Balances;
 	type CurrencyMatcher = ();
-	type TrustedLockers = ();
+	type TrustedLockers = TrustedLockers;
 	type SovereignAccountOf = LocationToAccountId;
 	type MaxLockers = ConstU32<8>;
 	type MaxRemoteLockConsumers = ConstU32<0>;
-- 
GitLab