Unverified Commit 8ea76695 authored by Gavin Wood's avatar Gavin Wood Committed by GitHub
Browse files

XCM: Allow reclaim of assets dropped from holding (#3727)

* XCM: Introduce AssetTrap

* Revert reversions

* Remove attempts at weighing and add test

* Less storage use for asset trapping

* Add missing file

* Fixes

* Fixes

* Formatting

* Fixes

* Docs

* Filter types to allow runtimes to dictate which assets/origins should be trapped

* Formatting

* Tests

* Formatting

* Fixes

* Docs
parent 8fab1d8f
Pipeline #154535 canceled with stages
in 6 minutes and 19 seconds
...@@ -1044,7 +1044,7 @@ mod tests { ...@@ -1044,7 +1044,7 @@ mod tests {
} }
#[test] #[test]
fn invalid_attest_transactions_are_recognised() { fn invalid_attest_transactions_are_recognized() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
let p = PrevalidateAttests::<Test>::new(); let p = PrevalidateAttests::<Test>::new();
let c = Call::Claims(ClaimsCall::attest(StatementKind::Regular.to_text().to_vec())); let c = Call::Claims(ClaimsCall::attest(StatementKind::Regular.to_text().to_vec()));
......
...@@ -1296,7 +1296,9 @@ impl xcm_executor::Config for XcmConfig { ...@@ -1296,7 +1296,9 @@ impl xcm_executor::Config for XcmConfig {
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>; type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
// The weight trader piggybacks on the existing transaction-fee conversion logic. // The weight trader piggybacks on the existing transaction-fee conversion logic.
type Trader = UsingComponents<WeightToFee, KsmLocation, AccountId, Balances, ToAuthor<Runtime>>; type Trader = UsingComponents<WeightToFee, KsmLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = (); type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetClaims = XcmPallet;
} }
parameter_types! { parameter_types! {
......
...@@ -669,7 +669,9 @@ impl xcm_executor::Config for XcmConfig { ...@@ -669,7 +669,9 @@ impl xcm_executor::Config for XcmConfig {
type Barrier = Barrier; type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>; type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
type Trader = UsingComponents<WeightToFee, RocLocation, AccountId, Balances, ToAuthor<Runtime>>; type Trader = UsingComponents<WeightToFee, RocLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = (); type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetClaims = XcmPallet;
} }
parameter_types! { parameter_types! {
......
...@@ -86,4 +86,6 @@ impl xcm_executor::Config for XcmConfig { ...@@ -86,4 +86,6 @@ impl xcm_executor::Config for XcmConfig {
type Weigher = FixedWeightBounds<super::BaseXcmWeight, super::Call, MaxInstructions>; type Weigher = FixedWeightBounds<super::BaseXcmWeight, super::Call, MaxInstructions>;
type Trader = DummyWeightTrader; type Trader = DummyWeightTrader;
type ResponseHandler = super::Xcm; type ResponseHandler = super::Xcm;
type AssetTrap = super::Xcm;
type AssetClaims = super::Xcm;
} }
...@@ -938,7 +938,9 @@ impl xcm_executor::Config for XcmConfig { ...@@ -938,7 +938,9 @@ impl xcm_executor::Config for XcmConfig {
type Barrier = Barrier; type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>; type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
type Trader = UsingComponents<WeightToFee, WndLocation, AccountId, Balances, ToAuthor<Runtime>>; type Trader = UsingComponents<WeightToFee, WndLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = (); type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetClaims = XcmPallet;
} }
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
......
...@@ -11,6 +11,7 @@ log = { version = "0.4.14", default-features = false } ...@@ -11,6 +11,7 @@ log = { version = "0.4.14", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
...@@ -20,7 +21,6 @@ xcm-executor = { path = "../xcm-executor", default-features = false } ...@@ -20,7 +21,6 @@ xcm-executor = { path = "../xcm-executor", default-features = false }
[dev-dependencies] [dev-dependencies]
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
polkadot-runtime-parachains = { path = "../../runtime/parachains" } polkadot-runtime-parachains = { path = "../../runtime/parachains" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
xcm-builder = { path = "../xcm-builder" } xcm-builder = { path = "../xcm-builder" }
polkadot-parachain = { path = "../../parachain" } polkadot-parachain = { path = "../../parachain" }
...@@ -31,6 +31,7 @@ std = [ ...@@ -31,6 +31,7 @@ std = [
"codec/std", "codec/std",
"serde", "serde",
"sp-std/std", "sp-std/std",
"sp-core/std",
"sp-runtime/std", "sp-runtime/std",
"frame-support/std", "frame-support/std",
"frame-system/std", "frame-system/std",
......
...@@ -50,8 +50,12 @@ pub mod pallet { ...@@ -50,8 +50,12 @@ pub mod pallet {
pallet_prelude::*, pallet_prelude::*,
}; };
use frame_system::{pallet_prelude::*, Config as SysConfig}; use frame_system::{pallet_prelude::*, Config as SysConfig};
use sp_runtime::traits::{AccountIdConversion, BlockNumberProvider}; use sp_core::H256;
use xcm_executor::traits::{InvertLocation, OnResponse, WeightBounds}; use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash};
use xcm_executor::{
traits::{ClaimAssets, DropAssets, InvertLocation, OnResponse, WeightBounds},
Assets,
};
#[pallet::pallet] #[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)] #[pallet::generate_store(pub(super) trait Store)]
...@@ -170,6 +174,10 @@ pub mod pallet { ...@@ -170,6 +174,10 @@ pub mod pallet {
/// ///
/// \[ id \] /// \[ id \]
ResponseTaken(QueryId), ResponseTaken(QueryId),
/// Some assets have been placed in an asset trap.
///
/// \[ hash, origin, assets \]
AssetsTrapped(H256, MultiLocation, VersionedMultiAssets),
} }
#[pallet::origin] #[pallet::origin]
...@@ -236,6 +244,14 @@ pub mod pallet { ...@@ -236,6 +244,14 @@ pub mod pallet {
pub(super) type Queries<T: Config> = pub(super) type Queries<T: Config> =
StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<T::BlockNumber>, OptionQuery>; StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<T::BlockNumber>, OptionQuery>;
/// The existing asset traps.
///
/// Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of
/// times this pair has been trapped (usually just 1 if it exists at all).
#[pallet::storage]
#[pallet::getter(fn asset_trap)]
pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
#[pallet::hooks] #[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {} impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
...@@ -553,8 +569,47 @@ pub mod pallet { ...@@ -553,8 +569,47 @@ pub mod pallet {
} }
} }
impl<T: Config> DropAssets for Pallet<T> {
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
if assets.is_empty() {
return 0
}
let versioned = VersionedMultiAssets::from(MultiAssets::from(assets));
let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
AssetTraps::<T>::mutate(hash, |n| *n += 1);
Self::deposit_event(Event::AssetsTrapped(hash, origin.clone(), versioned));
// TODO: Put the real weight in there.
0
}
}
impl<T: Config> ClaimAssets for Pallet<T> {
fn claim_assets(
origin: &MultiLocation,
ticket: &MultiLocation,
assets: &MultiAssets,
) -> bool {
let mut versioned = VersionedMultiAssets::from(assets.clone());
match (ticket.parents, &ticket.interior) {
(0, X1(GeneralIndex(i))) =>
versioned = match versioned.into_version(*i as u32) {
Ok(v) => v,
Err(()) => return false,
},
(0, Here) => (),
_ => return false,
};
let hash = BlakeTwo256::hash_of(&(origin, versioned));
match AssetTraps::<T>::get(hash) {
0 => return false,
1 => AssetTraps::<T>::remove(hash),
n => AssetTraps::<T>::insert(hash, n - 1),
}
return true
}
}
impl<T: Config> OnResponse for Pallet<T> { impl<T: Config> OnResponse for Pallet<T> {
/// Returns `true` if we are expecting a response from `origin` for query `query_id`.
fn expecting_response(origin: &MultiLocation, query_id: QueryId) -> bool { fn expecting_response(origin: &MultiLocation, query_id: QueryId) -> bool {
if let Some(QueryStatus::Pending { responder, .. }) = Queries::<T>::get(query_id) { if let Some(QueryStatus::Pending { responder, .. }) = Queries::<T>::get(query_id) {
return MultiLocation::try_from(responder).map_or(false, |r| origin == &r) return MultiLocation::try_from(responder).map_or(false, |r| origin == &r)
...@@ -562,7 +617,6 @@ pub mod pallet { ...@@ -562,7 +617,6 @@ pub mod pallet {
false false
} }
/// Handler for receiving a `response` from `origin` relating to `query_id`.
fn on_response( fn on_response(
origin: &MultiLocation, origin: &MultiLocation,
query_id: QueryId, query_id: QueryId,
......
...@@ -251,6 +251,8 @@ impl xcm_executor::Config for XcmConfig { ...@@ -251,6 +251,8 @@ impl xcm_executor::Config for XcmConfig {
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>; type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
type Trader = FixedRateOfFungible<CurrencyPerSecond, ()>; type Trader = FixedRateOfFungible<CurrencyPerSecond, ()>;
type ResponseHandler = XcmPallet; type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetClaims = XcmPallet;
} }
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, AnyNetwork>; pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, AnyNetwork>;
......
...@@ -14,11 +14,12 @@ ...@@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use crate::{mock::*, QueryStatus}; use crate::{mock::*, AssetTraps, QueryStatus};
use frame_support::{assert_noop, assert_ok, traits::Currency}; use frame_support::{assert_noop, assert_ok, traits::Currency};
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId}; use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
use sp_runtime::traits::{BlakeTwo256, Hash};
use std::convert::TryInto; use std::convert::TryInto;
use xcm::{latest::prelude::*, VersionedXcm}; use xcm::{latest::prelude::*, VersionedMultiAssets, VersionedXcm};
use xcm_executor::XcmExecutor; use xcm_executor::XcmExecutor;
const ALICE: AccountId = AccountId::new([0u8; 32]); const ALICE: AccountId = AccountId::new([0u8; 32]);
...@@ -295,3 +296,81 @@ fn execute_withdraw_to_deposit_works() { ...@@ -295,3 +296,81 @@ fn execute_withdraw_to_deposit_works() {
); );
}); });
} }
/// Test drop/claim assets.
#[test]
fn trapped_assets_can_be_claimed() {
let balances = vec![(ALICE, INITIAL_BALANCE), (BOB, INITIAL_BALANCE)];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = 6 * BaseXcmWeight::get();
let dest: MultiLocation =
Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }.into();
assert_ok!(XcmPallet::execute(
Origin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm(vec![
WithdrawAsset((Here, SEND_AMOUNT).into()),
buy_execution((Here, SEND_AMOUNT)),
// Don't propagated the error into the result.
SetErrorHandler(Xcm(vec![ClearError])),
// This will make an error.
Trap(0),
// This would succeed, but we never get to it.
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest.clone() },
]))),
weight
));
let source: MultiLocation =
Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }.into();
let trapped = AssetTraps::<Test>::iter().collect::<Vec<_>>();
let vma = VersionedMultiAssets::from(MultiAssets::from((Here, SEND_AMOUNT)));
let hash = BlakeTwo256::hash_of(&(source.clone(), vma.clone()));
assert_eq!(
last_events(2),
vec![
Event::XcmPallet(crate::Event::AssetsTrapped(hash.clone(), source, vma)),
Event::XcmPallet(crate::Event::Attempted(Outcome::Complete(
5 * BaseXcmWeight::get()
)))
]
);
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE);
let expected = vec![(hash, 1u32)];
assert_eq!(trapped, expected);
let weight = 3 * BaseXcmWeight::get();
assert_ok!(XcmPallet::execute(
Origin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest.clone() },
]))),
weight
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE + SEND_AMOUNT);
assert_eq!(AssetTraps::<Test>::iter().collect::<Vec<_>>(), vec![]);
let weight = 3 * BaseXcmWeight::get();
assert_ok!(XcmPallet::execute(
Origin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
]))),
weight
));
assert_eq!(
last_event(),
Event::XcmPallet(crate::Event::Attempted(Outcome::Incomplete(
BaseXcmWeight::get(),
XcmError::UnknownClaim
)))
);
});
}
...@@ -217,6 +217,16 @@ pub enum VersionedMultiAssets { ...@@ -217,6 +217,16 @@ pub enum VersionedMultiAssets {
V1(v1::MultiAssets), V1(v1::MultiAssets),
} }
impl VersionedMultiAssets {
pub fn into_version(self, n: u32) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<Vec<v0::MultiAsset>> for VersionedMultiAssets { impl From<Vec<v0::MultiAsset>> for VersionedMultiAssets {
fn from(x: Vec<v0::MultiAsset>) -> Self { fn from(x: Vec<v0::MultiAsset>) -> Self {
VersionedMultiAssets::V0(x) VersionedMultiAssets::V0(x)
......
...@@ -392,6 +392,8 @@ pub enum Instruction<Call> { ...@@ -392,6 +392,8 @@ pub enum Instruction<Call> {
/// prioritized under standard asset ordering. Any others will remain in holding. /// prioritized under standard asset ordering. Any others will remain in holding.
/// - `beneficiary`: The new owner for the assets. /// - `beneficiary`: The new owner for the assets.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation }, DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
...@@ -411,6 +413,8 @@ pub enum Instruction<Call> { ...@@ -411,6 +413,8 @@ pub enum Instruction<Call> {
/// - `xcm`: The orders that should follow the `ReserveAssetDeposited` instruction /// - `xcm`: The orders that should follow the `ReserveAssetDeposited` instruction
/// which is sent onwards to `dest`. /// which is sent onwards to `dest`.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
DepositReserveAsset { DepositReserveAsset {
assets: MultiAssetFilter, assets: MultiAssetFilter,
...@@ -428,6 +432,8 @@ pub enum Instruction<Call> { ...@@ -428,6 +432,8 @@ pub enum Instruction<Call> {
/// - `give`: The asset(s) to remove from holding. /// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets }, ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets },
...@@ -442,6 +448,8 @@ pub enum Instruction<Call> { ...@@ -442,6 +448,8 @@ pub enum Instruction<Call> {
/// - `xcm`: The instructions to execute on the assets once withdrawn *on the reserve /// - `xcm`: The instructions to execute on the assets once withdrawn *on the reserve
/// location*. /// location*.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, xcm: Xcm<()> }, InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, xcm: Xcm<()> },
...@@ -456,6 +464,8 @@ pub enum Instruction<Call> { ...@@ -456,6 +464,8 @@ pub enum Instruction<Call> {
/// NOTE: The `dest` location *MUST* respect this origin as a valid teleportation origin for all /// NOTE: The `dest` location *MUST* respect this origin as a valid teleportation origin for all
/// `assets`. If it does not, then the assets may be lost. /// `assets`. If it does not, then the assets may be lost.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> }, InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> },
...@@ -472,6 +482,8 @@ pub enum Instruction<Call> { ...@@ -472,6 +482,8 @@ pub enum Instruction<Call> {
/// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the
/// response may not execute at all. /// response may not execute at all.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
QueryHolding { QueryHolding {
#[codec(compact)] #[codec(compact)]
...@@ -490,13 +502,20 @@ pub enum Instruction<Call> { ...@@ -490,13 +502,20 @@ pub enum Instruction<Call> {
/// expected maximum weight of the total XCM to be executed for the /// expected maximum weight of the total XCM to be executed for the
/// `AllowTopLevelPaidExecutionFrom` barrier to allow the XCM be executed. /// `AllowTopLevelPaidExecutionFrom` barrier to allow the XCM be executed.
/// ///
/// Kind: *Instruction*
///
/// Errors: /// Errors:
BuyExecution { fees: MultiAsset, weight_limit: WeightLimit }, BuyExecution { fees: MultiAsset, weight_limit: WeightLimit },
/// Refund any surplus weight previously bought with `BuyExecution`. /// Refund any surplus weight previously bought with `BuyExecution`.
///
/// Kind: *Instruction*
///
/// Errors: None.
RefundSurplus, RefundSurplus,
/// Set code that should be called in the case of an error happening. /// Set the Error Handler Register. This is code that should be called in the case of an error
/// happening.
/// ///
/// An error occurring within execution of this code will _NOT_ result in the error register /// An error occurring within execution of this code will _NOT_ result in the error register
/// being set, nor will an error handler be called due to it. The error handler and appendix /// being set, nor will an error handler be called due to it. The error handler and appendix
...@@ -505,10 +524,15 @@ pub enum Instruction<Call> { ...@@ -505,10 +524,15 @@ pub enum Instruction<Call> {
/// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing
/// weight however includes only the difference between the previous handler and the new /// weight however includes only the difference between the previous handler and the new
/// handler, which can reasonably be negative, which would result in a surplus. /// handler, which can reasonably be negative, which would result in a surplus.
///
/// Kind: *Instruction*
///
/// Errors: None.
SetErrorHandler(Xcm<Call>), SetErrorHandler(Xcm<Call>),
/// Set code that should be called after code execution (including the error handler if any) /// Set the Appendix Register. This is code that should be called after code execution
/// is finished. This will be called regardless of whether an error occurred. /// (including the error handler if any) is finished. This will be called regardless of whether
/// an error occurred.
/// ///
/// Any error occurring due to execution of this code will result in the error register being /// Any error occurring due to execution of this code will result in the error register being
/// set, and the error handler (if set) firing. /// set, and the error handler (if set) firing.
...@@ -516,10 +540,38 @@ pub enum Instruction<Call> { ...@@ -516,10 +540,38 @@ pub enum Instruction<Call> {
/// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing
/// weight however includes only the difference between the previous appendix and the new /// weight however includes only the difference between the previous appendix and the new
/// appendix, which can reasonably be negative, which would result in a surplus. /// appendix, which can reasonably be negative, which would result in a surplus.
///
/// Kind: *Instruction*
///
/// Errors: None.
SetAppendix(Xcm<Call>), SetAppendix(Xcm<Call>),
/// Clear the error register. /// Clear the Error Register.
///
/// Kind: *Instruction*
///
/// Errors: None.
ClearError, ClearError,
/// Create some assets which are being held on behalf of the origin.
///
/// - `assets`: The assets which are to be claimed. This must match exactly with the assets
/// claimable by the origin of the ticket.
/// - `ticket`: The ticket of the asset; this is an abstract identifier to help locate the
/// asset.
///
/// Kind: *Instruction*
///
/// Errors:
ClaimAsset { assets: MultiAssets, ticket: MultiLocation },
/// Always throws an error of type `Trap`.
///
/// Kind: *Instruction*
///
/// Errors:
/// - `Trap`: All circumstances, whose inner value is the same as this item's inner value.
Trap(u64),
} }
impl<Call> Xcm<Call> { impl<Call> Xcm<Call> {
...@@ -572,6 +624,8 @@ impl<Call> Instruction<Call> { ...@@ -572,6 +624,8 @@ impl<Call> Instruction<Call> {
SetErrorHandler(xcm) => SetErrorHandler(xcm.into()), SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
SetAppendix(xcm) => SetAppendix(xcm.into()), SetAppendix(xcm) => SetAppendix(xcm.into()),
ClearError => ClearError, ClearError => ClearError,
ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
Trap(code) => Trap(code),
} }
} }
} }
......
...@@ -94,6 +94,10 @@ pub enum Error { ...@@ -94,6 +94,10 @@ pub enum Error {
Unroutable, Unroutable,
/// The weight required was not specified when it should have been. /// The weight required was not specified when it should have been.
UnknownWeightRequired, UnknownWeightRequired,
/// An error was intentionally forced. A code is included.
Trap(u64),
/// The given claim could not be recognized/found.
UnknownClaim,
} }
impl From<()> for Error { impl From<()> for Error {
......
...@@ -30,7 +30,7 @@ use xcm_executor::traits::{OnResponse, ShouldExecute}; ...@@ -30,7 +30,7 @@ use xcm_executor::traits::{OnResponse, ShouldExecute};
pub struct TakeWeightCredit; pub struct TakeWeightCredit;
impl ShouldExecute for TakeWeightCredit { impl ShouldExecute for TakeWeightCredit {
fn should_execute<Call>( fn should_execute<Call>(
_origin: &Option<MultiLocation>, _origin: &MultiLocation,
_top_level: bool, _top_level: bool,
_message: &mut Xcm<Call>, _message: &mut Xcm<Call>,
max_weight: Weight, max_weight: Weight,
...@@ -49,19 +49,21 @@ impl ShouldExecute for TakeWeightCredit { ...@@ -49,19 +49,21 @@ impl ShouldExecute for TakeWeightCredit {
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>); pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);