Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
XCM: PayOverXcm config (#6900)
Browse files Browse the repository at this point in the history
* Move XCM query functionality to trait

* Fix tests

* Add PayOverXcm implementation

* fix the PayOverXcm trait to compile

* moved doc comment out of trait implmeentation and to the trait

* PayOverXCM documentation

* Change documentation a bit

* Added empty benchmark methods implementation and changed docs

* update PayOverXCM to convert AccountIds to MultiLocations

* Implement benchmarking method

* Change v3 to latest

* Descend origin to an asset sender (#6970)

* descend origin to an asset sender

* sender as tuple of dest and sender

* Add more variants to the QueryResponseStatus enum

* Change Beneficiary to Into<[u8; 32]>

* update PayOverXcm to return concrete errors and use AccountId as sender

* use polkadot-primitives for AccountId

* fix dependency to use polkadot-core-primitives

* force Unpaid instruction to the top of the instructions list

* modify report_outcome to accept interior argument

* use new_query directly for building final xcm query, instead of report_outcome

* fix usage of new_query to use the XcmQueryHandler

* fix usage of new_query to use the XcmQueryHandler

* tiny method calling fix

* xcm query handler (#7198)

* drop redundant query status

* rename ReportQueryStatus to OuterQueryStatus

* revert rename of QueryResponseStatus

* update mapping

* Update xcm/xcm-builder/src/pay.rs

Co-authored-by: Gavin Wood <gavin@parity.io>

* Updates

* Docs

* Fix benchmarking stuff

* Destination can be determined based on asset_kind

* Tweaking API to minimise clones

* Some repotting and docs

---------

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>
Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
Co-authored-by: Anthony Alaribe <anthony.alaribe@parity.io>
Co-authored-by: Gavin Wood <gavin@parity.io>
  • Loading branch information
5 people committed May 31, 2023
1 parent ba1f654 commit b7a42f7
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 65 deletions.
3 changes: 2 additions & 1 deletion runtime/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ pub mod pallet_test_notifier {
use pallet_xcm::ensure_response;
use sp_runtime::DispatchResult;
use xcm::latest::prelude::*;
use xcm_executor::traits::QueryHandler as XcmQueryHandler;

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -581,7 +582,7 @@ pub mod pallet_test_notifier {
let id = who
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
.map_err(|_| Error::<T>::BadAccountFormat)?;
let qid = pallet_xcm::Pallet::<T>::new_query(
let qid = <pallet_xcm::Pallet<T> as XcmQueryHandler>::new_query(
Junction::AccountId32 { network: None, id },
100u32.into(),
Here,
Expand Down
117 changes: 62 additions & 55 deletions xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ use frame_system::pallet_prelude::*;
pub use pallet::*;
use xcm_executor::{
traits::{
CheckSuspension, ClaimAssets, DropAssets, MatchesFungible, OnResponse,
VersionChangeNotifier, WeightBounds,
CheckSuspension, ClaimAssets, DropAssets, MatchesFungible, OnResponse, QueryHandler,
QueryResponseStatus, VersionChangeNotifier, WeightBounds,
},
Assets,
};
Expand Down Expand Up @@ -1126,6 +1126,66 @@ pub mod pallet {
/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
const MAX_ASSETS_FOR_TRANSFER: usize = 2;

impl<T: Config> QueryHandler for Pallet<T> {
type QueryId = u64;
type BlockNumber = T::BlockNumber;
type Error = XcmError;
type UniversalLocation = T::UniversalLocation;

/// Attempt to create a new query ID and register it as a query that is yet to respond.
fn new_query(
responder: impl Into<MultiLocation>,
timeout: T::BlockNumber,
match_querier: impl Into<MultiLocation>,
) -> Self::QueryId {
Self::do_new_query(responder, None, timeout, match_querier).into()
}

/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
/// value.
fn report_outcome(
message: &mut Xcm<()>,
responder: impl Into<MultiLocation>,
timeout: Self::BlockNumber,
) -> Result<Self::QueryId, Self::Error> {
let responder = responder.into();
let destination = Self::UniversalLocation::get()
.invert_target(&responder)
.map_err(|()| XcmError::LocationNotInvertible)?;
let query_id = Self::new_query(responder, timeout, Here);
let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
let report_error = Xcm(vec![ReportError(response_info)]);
message.0.insert(0, SetAppendix(report_error));
Ok(query_id)
}

/// Removes response when ready and emits [Event::ResponseTaken] event.
fn take_response(query_id: Self::QueryId) -> QueryResponseStatus<Self::BlockNumber> {
match Queries::<T>::get(query_id) {
Some(QueryStatus::Ready { response, at }) => match response.try_into() {
Ok(response) => {
Queries::<T>::remove(query_id);
Self::deposit_event(Event::ResponseTaken { query_id });
QueryResponseStatus::Ready { response, at }
},
Err(_) => QueryResponseStatus::UnexpectedVersion,
},
Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
Some(_) => QueryResponseStatus::UnexpectedVersion,
None => QueryResponseStatus::NotFound,
}
}

#[cfg(feature = "runtime-benchmarks")]
fn expect_response(id: Self::QueryId, response: Response) {
let response = response.into();
Queries::<T>::insert(
id,
QueryStatus::Ready { response, at: frame_system::Pallet::<T>::block_number() },
);
}
}

impl<T: Config> Pallet<T> {
fn do_reserve_transfer_assets(
origin: OriginFor<T>,
Expand Down Expand Up @@ -1497,36 +1557,6 @@ impl<T: Config> Pallet<T> {
})
}

/// Consume `message` and return another which is equivalent to it except that it reports
/// back the outcome.
///
/// - `message`: The message whose outcome should be reported.
/// - `responder`: The origin from which a response should be expected.
/// - `timeout`: The block number after which it is permissible for `notify` not to be
/// called even if a response is received.
///
/// `report_outcome` may return an error if the `responder` is not invertible.
///
/// It is assumed that the querier of the response will be `Here`.
///
/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
/// value.
pub fn report_outcome(
message: &mut Xcm<()>,
responder: impl Into<MultiLocation>,
timeout: T::BlockNumber,
) -> Result<QueryId, XcmError> {
let responder = responder.into();
let destination = T::UniversalLocation::get()
.invert_target(&responder)
.map_err(|()| XcmError::LocationNotInvertible)?;
let query_id = Self::new_query(responder, timeout, Here);
let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
let report_error = Xcm(vec![ReportError(response_info)]);
message.0.insert(0, SetAppendix(report_error));
Ok(query_id)
}

/// Consume `message` and return another which is equivalent to it except that it reports
/// back the outcome and dispatches `notify` on this chain.
///
Expand Down Expand Up @@ -1568,15 +1598,6 @@ impl<T: Config> Pallet<T> {
Ok(())
}

/// Attempt to create a new query ID and register it as a query that is yet to respond.
pub fn new_query(
responder: impl Into<MultiLocation>,
timeout: T::BlockNumber,
match_querier: impl Into<MultiLocation>,
) -> u64 {
Self::do_new_query(responder, None, timeout, match_querier)
}

/// Attempt to create a new query ID and register it as a query that is yet to respond, and
/// which will call a dispatchable when a response happens.
pub fn new_notify_query(
Expand All @@ -1591,20 +1612,6 @@ impl<T: Config> Pallet<T> {
Self::do_new_query(responder, Some(notify), timeout, match_querier)
}

/// Attempt to remove and return the response of query with ID `query_id`.
///
/// Returns `None` if the response is not (yet) available.
pub fn take_response(query_id: QueryId) -> Option<(Response, T::BlockNumber)> {
if let Some(QueryStatus::Ready { response, at }) = Queries::<T>::get(query_id) {
let response = response.try_into().ok()?;
Queries::<T>::remove(query_id);
Self::deposit_event(Event::ResponseTaken { query_id });
Some((response, at))
} else {
None
}
}

/// Note that a particular destination to whom we would like to send a message is unknown
/// and queue it for version discovery.
fn note_unknown_version(dest: &MultiLocation) {
Expand Down
3 changes: 2 additions & 1 deletion xcm/pallet-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub mod pallet_test_notifier {
use frame_system::pallet_prelude::*;
use sp_runtime::DispatchResult;
use xcm::latest::prelude::*;
use xcm_executor::traits::QueryHandler;

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -85,7 +86,7 @@ pub mod pallet_test_notifier {
let id = who
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
.map_err(|_| Error::<T>::BadAccountFormat)?;
let qid = crate::Pallet::<T>::new_query(
let qid = <crate::Pallet<T> as QueryHandler>::new_query(
Junction::AccountId32 { network: None, id },
100u32.into(),
querier,
Expand Down
8 changes: 5 additions & 3 deletions xcm/pallet-xcm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash};
use xcm::{latest::QueryResponseInfo, prelude::*};
use xcm_builder::AllowKnownQueryResponses;
use xcm_executor::{
traits::{Properties, ShouldExecute},
traits::{Properties, QueryHandler, QueryResponseStatus, ShouldExecute},
XcmExecutor,
};

Expand Down Expand Up @@ -170,7 +170,8 @@ fn report_outcome_works() {
})
);

let response = Some((Response::ExecutionResult(None), 1));
let response =
QueryResponseStatus::Ready { response: Response::ExecutionResult(None), at: 1 };
assert_eq!(XcmPallet::take_response(0), response);
});
}
Expand Down Expand Up @@ -270,7 +271,8 @@ fn custom_querier_works() {
})
);

let response = Some((Response::ExecutionResult(None), 1));
let response =
QueryResponseStatus::Ready { response: Response::ExecutionResult(None), at: 1 };
assert_eq!(XcmPallet::take_response(0), response);
});
}
Expand Down
3 changes: 2 additions & 1 deletion xcm/xcm-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ polkadot-test-runtime = { path = "../../runtime/test-runtime" }
default = ["std"]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks"
"frame-system/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
]
std = [
"log/std",
Expand Down
8 changes: 6 additions & 2 deletions xcm/xcm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ pub mod test_utils;

mod location_conversion;
pub use location_conversion::{
Account32Hash, AccountId32Aliases, AccountKey20Aliases, ChildParachainConvertsVia,
GlobalConsensusParachainConvertsFor, ParentIsPreset, SiblingParachainConvertsVia,
Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32,
ChildParachainConvertsVia, GlobalConsensusParachainConvertsFor, ParentIsPreset,
SiblingParachainConvertsVia,
};

mod origin_conversion;
Expand Down Expand Up @@ -95,3 +96,6 @@ pub use universal_exports::{
ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable,
SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
};

mod pay;
pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm};
20 changes: 20 additions & 0 deletions xcm/xcm-builder/src/location_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,26 @@ impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]>
}
}

/// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a
/// `MultiLocation` consisting solely of a `AccountId32` junction with a fixed value for its
/// network (provided by `Network`) and the `AccountId`'s `[u8; 32]` datum for the `id`.
pub struct AliasesIntoAccountId32<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<'a, Network: Get<Option<NetworkId>>, AccountId: Clone + Into<[u8; 32]> + Clone>
Convert<&'a AccountId, MultiLocation> for AliasesIntoAccountId32<Network, AccountId>
{
fn convert(who: &AccountId) -> Result<MultiLocation, &'a AccountId> {
Ok(AccountId32 { network: Network::get(), id: who.clone().into() }.into())
}
}

impl<Network: Get<Option<NetworkId>>, AccountId: Into<[u8; 32]> + Clone>
Convert<AccountId, MultiLocation> for AliasesIntoAccountId32<Network, AccountId>
{
fn convert(who: AccountId) -> Result<MultiLocation, AccountId> {
Ok(AccountId32 { network: Network::get(), id: who.into() }.into())
}
}

pub struct AccountKey20Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
Convert<MultiLocation, AccountId> for AccountKey20Aliases<Network, AccountId>
Expand Down
Loading

0 comments on commit b7a42f7

Please sign in to comment.