Add Support for Foreign Assets (#2133)

* add foreign assets to westmint

* add foreign assets to statemine

* use updated api for ensure origin trait

* Assets/ForeignAssets tests and fixes (#2167)

* Test for create and transfer `TrustBackedAssets` with AssetTransactor

* Test for transfer `local Currency` with AssetTransactor

* Test for create foreign assets (covers foreign relaychain currency)

* Added `ForeignFungiblesTransactor` and test for transfer `ForeignAssets` with AssetTransactor

* Removed unused `pub const Local: MultiLocation`

* Changed `ParaId -> Sibling` for `SiblingParachainConvertsVia`

* Test for create foreign assets (covers local sibling parachain assets)

* Reverted stuff for ForeignCreators from different global consensus (moved to transfer asset branch)

* Refactor `weight_limit` for `execute_xcm`

* Added test for `set_metadata` by ForeignCreator with `xcm::Transact(set_metadata)`

* Renamed `receive_teleported_asset_works` -> `receive_teleported_asset_for_native_asset_works`

* Allow `ForeignCreators` only for sibling parachains

* Unify ReservedDmpWeight/ReservedXcmpWeight usage

* Removed hack - replaced with `MatchedConvertedConcreteId`

* Refactor `ForeignCreators` to assets-common

* Add `ReceiveTeleportedAsset` test

* Change test - `Utility::batch` -> Multiple `xcm::Transact`

* Reusing the same deposits as for TrustBackedAssets

* missing `try_successful_origin` ?

* Finished `ForeignAssets` for westmint (converter, FungiblesApi, tests)

* Refactoring tests - receive_teleported_asset_for_native_asset_works

* ForeignAssets for statemine + refactored `receive_teleported_asset_from_foreign_creator_works`

* Add `ForeignAssets` to statemine `FungiblesApi`

* Add `asset_transactor_transfer_with_local_consensus_currency_works` to all runtimes

* Added `asset_transactor_transfer_with_trust_backed_assets_works` test

* Added `asset_transactor_transfer_with_foreign_assets_works`

* Fix `missing `try_successful_origin` in implementation`

* Added `create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works`

* Added `ExpectTransactStatus` check

* Small rename

* Extended `test_assets_balances_api_works` with ForeignAssets for `statemine`

* PR fixes

* Update parachains/runtimes/assets/test-utils/src/test_cases.rs

---------

Co-authored-by: parity-processbot <>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Added `StartsWithExplicitGlobalConsensus` to ignores (#2338)

* Update parachains/runtimes/assets/common/src/lib.rs

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

* include mint and burn in SafeCallFilter

* include mint and burn in SafeCallFilter (statemine)

* clarify doc

* Fix compilation (moved trait `InspectMetadata`)

* Fix test

* Extended test for `teleport` from/to relaychain + `CheckingAccount` (Part I)

* Extended test for `teleport` from/to foreign parachain + `CheckingAccount` (Part II)

* Fixed TODO - `NonLocal` for `ForeignAssets`

* Changed `NonLocal` to `NoChecking`

* Fix weight in test

---------

Co-authored-by: parity-processbot <>
Co-authored-by: muharem <ismailov.m.h@gmail.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Gavin Wood <gavin@parity.io>
This commit is contained in:
joe petrowski
2023-03-23 15:14:27 +01:00
committed by GitHub
parent 44126eb857
commit af46f4b41e
25 changed files with 2865 additions and 237 deletions
@@ -1,11 +1,27 @@
use frame_support::traits::GenesisBuild;
use sp_std::marker::PhantomData;
use frame_support::traits::OriginTrait;
use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData};
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use frame_support::{
dispatch::{DispatchResult, RawOrigin, UnfilteredDispatchable},
inherent::{InherentData, ProvideInherent},
traits::{GenesisBuild, OriginTrait},
weights::Weight,
};
use parachains_common::AccountId;
use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber};
use sp_consensus_aura::AURA_ENGINE_ID;
use sp_core::Encode;
use sp_runtime::{Digest, DigestItem};
use xcm::{
latest::{MultiAsset, MultiLocation, XcmContext, XcmHash},
prelude::{Concrete, Fungible, Outcome, XcmError, XcmVersion},
};
use xcm_executor::{traits::TransactAsset, Assets};
pub mod test_cases;
pub use test_cases::CollatorSessionKeys;
pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance;
pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId;
@@ -14,7 +30,11 @@ pub type SessionKeysOf<Runtime> = <Runtime as pallet_session::Config>::Keys;
// Basic builder based on balances, collators and pallet_sessopm
pub struct ExtBuilder<
Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config,
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> {
// endowed accounts with balances
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
@@ -22,19 +42,40 @@ pub struct ExtBuilder<
collators: Vec<AccountIdOf<Runtime>>,
// keys added to pallet session
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
// safe xcm version for pallet_xcm
safe_xcm_version: Option<XcmVersion>,
// para id
para_id: Option<ParaId>,
_runtime: PhantomData<Runtime>,
}
impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config> Default
for ExtBuilder<Runtime>
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> Default for ExtBuilder<Runtime>
{
fn default() -> ExtBuilder<Runtime> {
ExtBuilder { balances: vec![], collators: vec![], keys: vec![], _runtime: PhantomData }
ExtBuilder {
balances: vec![],
collators: vec![],
keys: vec![],
safe_xcm_version: None,
para_id: None,
_runtime: PhantomData,
}
}
}
impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config>
ExtBuilder<Runtime>
impl<
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config,
> ExtBuilder<Runtime>
{
pub fn with_balances(
mut self,
@@ -56,6 +97,21 @@ impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::C
self
}
pub fn with_tracing(self) -> Self {
frame_support::sp_tracing::try_init_simple();
self
}
pub fn with_safe_xcm_version(mut self, safe_xcm_version: XcmVersion) -> Self {
self.safe_xcm_version = Some(safe_xcm_version);
self
}
pub fn with_para_id(mut self, para_id: ParaId) -> Self {
self.para_id = Some(para_id);
self
}
pub fn build(self) -> sp_io::TestExternalities
where
Runtime:
@@ -64,6 +120,20 @@ impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::C
{
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
<pallet_xcm::GenesisConfig as GenesisBuild<Runtime>>::assimilate_storage(
&pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version },
&mut t,
)
.unwrap();
if let Some(para_id) = self.para_id {
<parachain_info::GenesisConfig as frame_support::traits::GenesisBuild<Runtime>>::assimilate_storage(
&parachain_info::GenesisConfig { parachain_id: para_id },
&mut t,
)
.unwrap();
}
pallet_balances::GenesisConfig::<Runtime> { balances: self.balances.into() }
.assimilate_storage(&mut t)
.unwrap();
@@ -132,3 +202,184 @@ where
<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id.into())
}
}
impl<XcmConfig: xcm_executor::Config> RuntimeHelper<XcmConfig> {
pub fn do_transfer(
from: MultiLocation,
to: MultiLocation,
(asset, amount): (MultiLocation, u128),
) -> Result<Assets, XcmError> {
<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
&MultiAsset { id: Concrete(asset), fun: Fungible(amount) },
&from,
&to,
// We aren't able to track the XCM that initiated the fee deposit, so we create a
// fake message hash here
&XcmContext::with_message_hash([0; 32]),
)
}
}
impl<Runtime: pallet_xcm::Config + cumulus_pallet_parachain_system::Config> RuntimeHelper<Runtime> {
pub fn do_teleport_assets<HrmpChannelOpener>(
origin: <Runtime as frame_system::Config>::RuntimeOrigin,
dest: MultiLocation,
beneficiary: MultiLocation,
(asset, amount): (MultiLocation, u128),
open_hrmp_channel: Option<(u32, u32)>,
) -> DispatchResult
where
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
{
// open hrmp (if needed)
if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
source_para_id.into(),
target_para_id.into(),
);
}
// do teleport
<pallet_xcm::Pallet<Runtime>>::teleport_assets(
origin,
Box::new(dest.into()),
Box::new(beneficiary.into()),
Box::new((Concrete(asset), amount).into()),
0,
)
}
}
pub enum XcmReceivedFrom {
Parent,
Sibling,
}
impl<ParachainSystem: cumulus_pallet_parachain_system::Config> RuntimeHelper<ParachainSystem> {
pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
use frame_support::traits::Get;
match from {
XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(),
XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(),
}
}
}
impl<Runtime: frame_system::Config + pallet_xcm::Config> RuntimeHelper<Runtime> {
pub fn assert_pallet_xcm_event_outcome(
unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
assert_outcome: fn(Outcome),
) {
let outcome = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
.find_map(|e| match e {
pallet_xcm::Event::Attempted(outcome) => Some(outcome),
_ => None,
});
match outcome {
Some(outcome) => assert_outcome(outcome),
None => assert!(false, "No `pallet_xcm::Event::Attempted(outcome)` event found!"),
}
}
}
impl<Runtime: frame_system::Config + cumulus_pallet_xcmp_queue::Config> RuntimeHelper<Runtime> {
pub fn xcmp_queue_message_sent(
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
) -> Option<XcmHash> {
<frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => message_hash,
_ => None,
})
}
}
pub fn assert_metadata<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_name: &str,
expected_symbol: &str,
expected_decimals: u8,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::name(asset_id.into()), Vec::from(expected_name),);
assert_eq!(Fungibles::symbol(asset_id.into()), Vec::from(expected_symbol),);
assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
}
pub fn assert_total<Fungibles, AccountId>(
asset_id: impl Into<Fungibles::AssetId> + Copy,
expected_total_issuance: impl Into<Fungibles::Balance>,
expected_active_issuance: impl Into<Fungibles::Balance>,
) where
Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect<AccountId>
+ frame_support::traits::tokens::fungibles::Inspect<AccountId>,
{
assert_eq!(Fungibles::total_issuance(asset_id.into()), expected_total_issuance.into());
assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
}
/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass
pub fn mock_open_hrmp_channel<
C: cumulus_pallet_parachain_system::Config,
T: ProvideInherent<Call = cumulus_pallet_parachain_system::Call<C>>,
>(
sender: ParaId,
recipient: ParaId,
) {
let n = 1_u32;
let mut sproof_builder = RelayStateSproofBuilder::default();
sproof_builder.para_id = sender;
sproof_builder.hrmp_channels.insert(
HrmpChannelId { sender, recipient },
AbridgedHrmpChannel {
max_capacity: 10,
max_total_size: 10_000_000_u32,
max_message_size: 10_000_000_u32,
msg_count: 0,
total_size: 0_u32,
mqc_head: None,
},
);
sproof_builder.hrmp_egress_channel_index = Some(vec![recipient]);
let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
let vfp = PersistedValidationData {
relay_parent_number: n as RelayChainBlockNumber,
relay_parent_storage_root,
..Default::default()
};
// It is insufficient to push the validation function params
// to storage; they must also be included in the inherent data.
let inherent_data = {
let mut inherent_data = InherentData::default();
let system_inherent_data = ParachainInherentData {
validation_data: vfp.clone(),
relay_chain_state,
downward_messages: Default::default(),
horizontal_messages: Default::default(),
};
inherent_data
.put_data(
cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
&system_inherent_data,
)
.expect("failed to put VFP inherent");
inherent_data
};
// execute the block
T::create_inherent(&inherent_data)
.expect("got an inherent")
.dispatch_bypass_filter(RawOrigin::None.into())
.expect("dispatch succeeded");
}
File diff suppressed because it is too large Load Diff