#![allow(unused)]fnmain() {
//! # Fundamentals lesson 5: XCM Executor//!//! Create your own executor for XCM.use super::holding::*;
use sp_std::{marker::PhantomData, prelude::*};
use xcm::latest::prelude::*;
use xcm_executor::traits::{ProcessTransaction, TransactAsset};
pubtraitXcmConfig {
/// How to withdraw and deposit an asset.typeAssetTransactor: TransactAsset;
/// Transactional processor for XCM instructions.typeTransactionalProcessor: ProcessTransaction;
}
/// The heart of the XCM Virtual Machine.pubstructXcmExecutor<Config: XcmConfig> {
/// The asset holding registrar, where we keep track of assets being processed by the XCM/// Executor.pub holding: AssetsInHolding,
/// Contextual data pertaining to a specific list of XCM instructions. Most relevant the/// `origin` of the XCM Message.pub context: XcmContext,
/// Just a placeholder to allow Rust to let us keep `Config`.
_config: PhantomData<Config>,
}
/// The implementation of the XCM Executor and how it processes XCM.impl<Config: XcmConfig> XcmExecutor<Config> {
/// Crete an initialize a new XCM Executor.pubfnnew(origin: implInto<Location>) -> Self {
let origin = origin.into();
// In our version of the XCM Executor, we ignore `message_id` and `topic`.let context =
XcmContext { origin: Some(origin), message_id: Default::default(), topic: None };
Self { holding: Default::default(), context, _config: PhantomData }
}
/// Process an entire XCM program, instruction by instruction.pubfnprocess(&mutself, xcm: Xcm<()>) -> Result<(), XcmError> {
log::trace!(target: "xcm::process", "xcm: {:?}", xcm);
for instruction in xcm.0.into_iter() {
self.process_instruction(instruction)?;
}
Ok(())
}
/// Simple helper function to access the `origin` from the XCM Executor `context`.pubfnorigin_ref(&self) -> Option<&Location> {
self.context.origin.as_ref()
}
/// Process a single XCM instruction, mutating the state of the XCM virtual machine.fnprocess_instruction(&mutself, instr: Instruction<()>) -> Result<(), XcmError> {
log::trace!(target: "xcm::process_instruction", "=== {:?}", instr);
match instr {
// Clear the origin.//// This may be used by the XCM author to ensure that later instructions cannot command// the authority of the origin (e.g. if they are being relayed from an untrusted// source, as often the case with `ReserveAssetDeposited`).
ClearOrigin => {
self.context.origin = None;
Ok(())
},
// Appends `who` to the current XCM Executor `origin` location.
DescendOrigin(who) => self
.context
.origin
.as_mut()
.ok_or(XcmError::BadOrigin)?
.append_with(who)
.map_err(|_| XcmError::LocationFull),
// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent// assets under the ownership of `beneficiary`.//// - `assets`: The asset(s) to be withdrawn.// - `beneficiary`: The new owner for the assets.
TransferAsset { assets, beneficiary } => {
Config::TransactionalProcessor::process(|| {
// Take `assets` from the origin account (on-chain) and place into dest account.let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
// Transfer each asset using the `AssetTransactor`.for asset in assets.inner() {
Config::AssetTransactor::transfer_asset(
&asset,
origin,
&beneficiary,
&self.context,
)?;
}
Ok(())
})
},
// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the// Holding Register.//// - `assets`: The asset(s) to be withdrawn into holding.
WithdrawAsset(assets) => {
/* TODO:
- Process everything inside a `TransactionalProcessor`.
- Get the `origin` or return `XcmError::BadOrigin`.
- For each `asset` in `assets`
- Use the `AssetTransactor` to `withdraw_asset`.
- `and_then`, if everything goes okay...
- `subsume_assets` into the `self.holding`
*/
todo!("{:?}", assets)
},
// Reduce Holding by up to the given assets.//// Holding is reduced by as much as possible up to the assets in the parameter. It is// not an error if the Holding does not contain the assets (to make this an error, use// `ExpectAsset` prior).
BurnAsset(assets) => {
/* TODO: Simply `saturating_take` `assets` from the `self.holding`. */
todo!("{:?}", assets)
},
// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets// under the ownership of `beneficiary` within this consensus system.//// - `assets`: The asset(s) to remove from holding.// - `beneficiary`: The new owner for the assets.
DepositAsset { assets, beneficiary } => {
todo!("{:?} {:?}", assets, beneficiary)
},
// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets// should be created and placed into the Holding Register.//// - `assets`: The asset(s) that are minted into the Holding Register.
ReceiveTeleportedAsset(assets) => {
todo!("{:?}", assets)
},
// In this workshop, we won't be implementing every instruction, just the ones above...// Our executor will simply panic if you try to execute other instructions.
_ => unimplemented!(),
}
}
}
/// A public trait allowing other systems to access and use the `XcmExecutor`.pubtraitExecuteXcm {
/// Execute an XCM from a given `origin`.fnexecute(origin: implInto<Location>, xcm: Xcm<()>) -> XcmResult;
}
impl<Config: XcmConfig> ExecuteXcm for XcmExecutor<Config> {
/// Execute an XCM from a given `origin`.fnexecute(origin: implInto<Location>, xcm: Xcm<()>) -> XcmResult {
log::trace!(target: "xcm::execute", "xcm: {:?}", xcm);
todo!("{:?}", origin.into())
}
}
}
#![allow(unused)]fnmain() {
//! # Fundamentals lesson 5: XCM Executor//!//! Create your own executor for XCM.use super::holding::*;
use sp_std::{marker::PhantomData, prelude::*};
use xcm::latest::prelude::*;
use xcm_executor::traits::{ProcessTransaction, TransactAsset};
pubtraitXcmConfig {
/// How to withdraw and deposit an asset.typeAssetTransactor: TransactAsset;
/// Transactional processor for XCM instructions.typeTransactionalProcessor: ProcessTransaction;
}
/// The heart of the XCM Virtual Machine.pubstructXcmExecutor<Config: XcmConfig> {
/// The asset holding registrar, where we keep track of assets being processed by the XCM/// Executor.pub holding: AssetsInHolding,
/// Contextual data pertaining to a specific list of XCM instructions. Most relevant the/// `origin` of the XCM Message.pub context: XcmContext,
/// Just a placeholder to allow Rust to let us keep `Config`.
_config: PhantomData<Config>,
}
/// The implementation of the XCM Executor and how it processes XCM.impl<Config: XcmConfig> XcmExecutor<Config> {
/// Crete an initialize a new XCM Executor.pubfnnew(origin: implInto<Location>) -> Self {
let origin = origin.into();
// In our version of the XCM Executor, we ignore `message_id` and `topic`.let context =
XcmContext { origin: Some(origin), message_id: Default::default(), topic: None };
Self { holding: Default::default(), context, _config: PhantomData }
}
/// Process an entire XCM program, instruction by instruction.pubfnprocess(&mutself, xcm: Xcm<()>) -> Result<(), XcmError> {
log::trace!(target: "xcm::process", "xcm: {:?}", xcm);
for instruction in xcm.0.into_iter() {
self.process_instruction(instruction)?;
}
Ok(())
}
/// Simple helper function to access the `origin` from the XCM Executor `context`.pubfnorigin_ref(&self) -> Option<&Location> {
self.context.origin.as_ref()
}
/// Process a single XCM instruction, mutating the state of the XCM virtual machine.fnprocess_instruction(&mutself, instr: Instruction<()>) -> Result<(), XcmError> {
log::trace!(target: "xcm::process_instruction", "=== {:?}", instr);
match instr {
// Clear the origin.//// This may be used by the XCM author to ensure that later instructions cannot command// the authority of the origin (e.g. if they are being relayed from an untrusted// source, as often the case with `ReserveAssetDeposited`).
ClearOrigin => {
self.context.origin = None;
Ok(())
},
// Appends `who` to the current XCM Executor `origin` location.
DescendOrigin(who) => self
.context
.origin
.as_mut()
.ok_or(XcmError::BadOrigin)?
.append_with(who)
.map_err(|_| XcmError::LocationFull),
// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent// assets under the ownership of `beneficiary`.//// - `assets`: The asset(s) to be withdrawn.// - `beneficiary`: The new owner for the assets.
TransferAsset { assets, beneficiary } => {
Config::TransactionalProcessor::process(|| {
// Take `assets` from the origin account (on-chain) and place into dest account.let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
// Transfer each asset using the `AssetTransactor`.for asset in assets.inner() {
Config::AssetTransactor::transfer_asset(
&asset,
origin,
&beneficiary,
&self.context,
)?;
}
Ok(())
})
},
// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the// Holding Register.//// - `assets`: The asset(s) to be withdrawn into holding.
WithdrawAsset(assets) => {
Config::TransactionalProcessor::process(|| {
let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
// Take `assets` from the origin account (on-chain)...for asset in assets.inner() {
Config::AssetTransactor::withdraw_asset(
asset,
origin,
Some(&self.context),
)?;
}
Ok(())
})
.and_then(|_| {
// ...and place into holding.self.holding.subsume_assets(assets.into());
Ok(())
})
},
// Reduce Holding by up to the given assets.//// Holding is reduced by as much as possible up to the assets in the parameter. It is// not an error if the Holding does not contain the assets (to make this an error, use// `ExpectAsset` prior).
BurnAsset(assets) => {
self.holding.saturating_take(assets.into());
Ok(())
},
// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets// under the ownership of `beneficiary` within this consensus system.//// - `assets`: The asset(s) to remove from holding.// - `beneficiary`: The new owner for the assets.
DepositAsset { assets, beneficiary } => {
todo!("{:?} {:?}", assets, beneficiary)
},
// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets// should be created and placed into the Holding Register.//// - `assets`: The asset(s) that are minted into the Holding Register.
ReceiveTeleportedAsset(assets) => {
todo!("{:?}", assets)
},
// In this workshop, we won't be implementing every instruction, just the ones above...// Our executor will simply panic if you try to execute other instructions.
_ => unimplemented!(),
}
}
}
/// A public trait allowing other systems to access and use the `XcmExecutor`.pubtraitExecuteXcm {
/// Execute an XCM from a given `origin`.fnexecute(origin: implInto<Location>, xcm: Xcm<()>) -> XcmResult;
}
impl<Config: XcmConfig> ExecuteXcm for XcmExecutor<Config> {
/// Execute an XCM from a given `origin`.fnexecute(origin: implInto<Location>, xcm: Xcm<()>) -> XcmResult {
log::trace!(target: "xcm::execute", "xcm: {:?}", xcm);
todo!("{:?}", origin.into())
}
}
}
diff --git a/fundamentals/src/xcm_executor.rs b/fundamentals/src/xcm_executor.rs
index f84c243..f97757d 100644
--- a/fundamentals/src/xcm_executor.rs+++ b/fundamentals/src/xcm_executor.rs
@@ -99,6 +99,14 @@ impl<Config: XcmConfig> XcmExecutor<Config> {
//
// - `assets`: The asset(s) to be withdrawn into holding.
WithdrawAsset(assets) => {
+ /* TODO:+ - Process everything inside a `TransactionalProcessor`.+ - Get the `origin` or return `XcmError::BadOrigin`.+ - For each `asset` in `assets`+ - Use the `AssetTransactor` to `withdraw_asset`.+ - `and_then`, if everything goes okay...+ - `subsume_assets` into the `self.holding`+ */
todo!("{:?}", assets)
},
// Reduce Holding by up to the given assets.
@@ -107,6 +115,7 @@ impl<Config: XcmConfig> XcmExecutor<Config> {
// not an error if the Holding does not contain the assets (to make this an error, use
// `ExpectAsset` prior).
BurnAsset(assets) => {
+ /* TODO: Simply `saturating_take` `assets` from the `self.holding`. */
todo!("{:?}", assets)
},
// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets
diff --git a/fundamentals/src/xcm_executor.rs b/fundamentals/src/xcm_executor.rs
index f97757d..d50d85a 100644
--- a/fundamentals/src/xcm_executor.rs+++ b/fundamentals/src/xcm_executor.rs
@@ -99,15 +99,23 @@ impl<Config: XcmConfig> XcmExecutor<Config> {
//
// - `assets`: The asset(s) to be withdrawn into holding.
WithdrawAsset(assets) => {
- /* TODO:- - Process everything inside a `TransactionalProcessor`.- - Get the `origin` or return `XcmError::BadOrigin`.- - For each `asset` in `assets`- - Use the `AssetTransactor` to `withdraw_asset`.- - `and_then`, if everything goes okay...- - `subsume_assets` into the `self.holding`- */- todo!("{:?}", assets)+ Config::TransactionalProcessor::process(|| {+ let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;+ // Take `assets` from the origin account (on-chain)...+ for asset in assets.inner() {+ Config::AssetTransactor::withdraw_asset(+ asset,+ origin,+ Some(&self.context),+ )?;+ }+ Ok(())+ })+ .and_then(|_| {+ // ...and place into holding.+ self.holding.subsume_assets(assets.into());+ Ok(())+ })
},
// Reduce Holding by up to the given assets.
//
@@ -115,8 +123,8 @@ impl<Config: XcmConfig> XcmExecutor<Config> {
// not an error if the Holding does not contain the assets (to make this an error, use
// `ExpectAsset` prior).
BurnAsset(assets) => {
- /* TODO: Simply `saturating_take` `assets` from the `self.holding`. */- todo!("{:?}", assets)+ self.holding.saturating_take(assets.into());+ Ok(())
},
// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets
// under the ownership of `beneficiary` within this consensus system.