mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-23 04:55:41 +00:00
xcm: Improve debuggability (#2799)
Adds more logging to the XCM execution for better debugging.
This commit is contained in:
Generated
+1
@@ -19143,6 +19143,7 @@ version = "1.0.0"
|
|||||||
name = "staging-xcm"
|
name = "staging-xcm"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"array-bytes 6.1.0",
|
||||||
"bounded-collections",
|
"bounded-collections",
|
||||||
"derivative",
|
"derivative",
|
||||||
"environmental",
|
"environmental",
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|||||||
spec_name: create_runtime_str!("coretime-rococo"),
|
spec_name: create_runtime_str!("coretime-rococo"),
|
||||||
impl_name: create_runtime_str!("coretime-rococo"),
|
impl_name: create_runtime_str!("coretime-rococo"),
|
||||||
authoring_version: 1,
|
authoring_version: 1,
|
||||||
spec_version: 1_005_002,
|
spec_version: 1_005_003,
|
||||||
impl_version: 0,
|
impl_version: 0,
|
||||||
apis: RUNTIME_API_VERSIONS,
|
apis: RUNTIME_API_VERSIONS,
|
||||||
transaction_version: 0,
|
transaction_version: 0,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ license.workspace = true
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
array-bytes = "6.1"
|
||||||
bounded-collections = { version = "0.1.9", default-features = false, features = ["serde"] }
|
bounded-collections = { version = "0.1.9", default-features = false, features = ["serde"] }
|
||||||
derivative = { version = "2.2.0", default-features = false, features = ["use_core"] }
|
derivative = { version = "2.2.0", default-features = false, features = ["use_core"] }
|
||||||
impl-trait-for-tuples = "0.2.2"
|
impl-trait-for-tuples = "0.2.2"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl<T> Eq for DoubleEncoded<T> {}
|
|||||||
|
|
||||||
impl<T> core::fmt::Debug for DoubleEncoded<T> {
|
impl<T> core::fmt::Debug for DoubleEncoded<T> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
self.encoded.fmt(f)
|
array_bytes::bytes2hex("0x", &self.encoded).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,15 @@
|
|||||||
|
|
||||||
//! Implementation of `ProcessMessage` for an `ExecuteXcm` implementation.
|
//! Implementation of `ProcessMessage` for an `ExecuteXcm` implementation.
|
||||||
|
|
||||||
use frame_support::{
|
use frame_support::traits::{ProcessMessage, ProcessMessageError};
|
||||||
ensure,
|
|
||||||
traits::{ProcessMessage, ProcessMessageError},
|
|
||||||
};
|
|
||||||
use parity_scale_codec::{Decode, FullCodec, MaxEncodedLen};
|
use parity_scale_codec::{Decode, FullCodec, MaxEncodedLen};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_std::{fmt::Debug, marker::PhantomData};
|
use sp_std::{fmt::Debug, marker::PhantomData};
|
||||||
use sp_weights::{Weight, WeightMeter};
|
use sp_weights::{Weight, WeightMeter};
|
||||||
use xcm::prelude::*;
|
use xcm::prelude::*;
|
||||||
|
|
||||||
|
const LOG_TARGET: &str = "xcm::process-message";
|
||||||
|
|
||||||
/// A message processor that delegates execution to an `XcmExecutor`.
|
/// A message processor that delegates execution to an `XcmExecutor`.
|
||||||
pub struct ProcessXcmMessage<MessageOrigin, XcmExecutor, Call>(
|
pub struct ProcessXcmMessage<MessageOrigin, XcmExecutor, Call>(
|
||||||
PhantomData<(MessageOrigin, XcmExecutor, Call)>,
|
PhantomData<(MessageOrigin, XcmExecutor, Call)>,
|
||||||
@@ -45,21 +44,68 @@ impl<
|
|||||||
meter: &mut WeightMeter,
|
meter: &mut WeightMeter,
|
||||||
id: &mut XcmHash,
|
id: &mut XcmHash,
|
||||||
) -> Result<bool, ProcessMessageError> {
|
) -> Result<bool, ProcessMessageError> {
|
||||||
let versioned_message = VersionedXcm::<Call>::decode(&mut &message[..])
|
let versioned_message = VersionedXcm::<Call>::decode(&mut &message[..]).map_err(|e| {
|
||||||
.map_err(|_| ProcessMessageError::Corrupt)?;
|
log::trace!(
|
||||||
let message = Xcm::<Call>::try_from(versioned_message)
|
target: LOG_TARGET,
|
||||||
.map_err(|_| ProcessMessageError::Unsupported)?;
|
"`VersionedXcm` failed to decode: {e:?}",
|
||||||
let pre = XcmExecutor::prepare(message).map_err(|_| ProcessMessageError::Unsupported)?;
|
);
|
||||||
|
|
||||||
|
ProcessMessageError::Corrupt
|
||||||
|
})?;
|
||||||
|
let message = Xcm::<Call>::try_from(versioned_message).map_err(|_| {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"Failed to convert `VersionedXcm` into `XcmV3`.",
|
||||||
|
);
|
||||||
|
|
||||||
|
ProcessMessageError::Unsupported
|
||||||
|
})?;
|
||||||
|
let pre = XcmExecutor::prepare(message).map_err(|_| {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"Failed to prepare message.",
|
||||||
|
);
|
||||||
|
|
||||||
|
ProcessMessageError::Unsupported
|
||||||
|
})?;
|
||||||
// The worst-case weight:
|
// The worst-case weight:
|
||||||
let required = pre.weight_of();
|
let required = pre.weight_of();
|
||||||
ensure!(meter.can_consume(required), ProcessMessageError::Overweight(required));
|
if !meter.can_consume(required) {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"Xcm required {required} more than remaining {}",
|
||||||
|
meter.remaining(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(ProcessMessageError::Overweight(required))
|
||||||
|
}
|
||||||
|
|
||||||
let (consumed, result) = match XcmExecutor::execute(origin.into(), pre, id, Weight::zero())
|
let (consumed, result) = match XcmExecutor::execute(origin.into(), pre, id, Weight::zero())
|
||||||
{
|
{
|
||||||
Outcome::Complete(w) => (w, Ok(true)),
|
Outcome::Complete(w) => {
|
||||||
Outcome::Incomplete(w, _) => (w, Ok(false)),
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"XCM message execution complete, used weight: {w}",
|
||||||
|
);
|
||||||
|
(w, Ok(true))
|
||||||
|
},
|
||||||
|
Outcome::Incomplete(w, e) => {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"XCM message execution incomplete, used weight: {w}, error: {e:?}",
|
||||||
|
);
|
||||||
|
|
||||||
|
(w, Ok(false))
|
||||||
|
},
|
||||||
// In the error-case we assume the worst case and consume all possible weight.
|
// In the error-case we assume the worst case and consume all possible weight.
|
||||||
Outcome::Error(_) => (required, Err(ProcessMessageError::Unsupported)),
|
Outcome::Error(e) => {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"XCM message execution error: {e:?}",
|
||||||
|
);
|
||||||
|
|
||||||
|
(required, Err(ProcessMessageError::Unsupported))
|
||||||
|
},
|
||||||
};
|
};
|
||||||
meter.consume(consumed);
|
meter.consume(consumed);
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ pub use xcm_executor::{
|
|||||||
Assets, Config,
|
Assets, Config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TestOrigin {
|
pub enum TestOrigin {
|
||||||
Root,
|
Root,
|
||||||
Relay,
|
Relay,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use frame_support::{
|
|||||||
use parity_scale_codec::{Decode, Encode};
|
use parity_scale_codec::{Decode, Encode};
|
||||||
use sp_core::defer;
|
use sp_core::defer;
|
||||||
use sp_io::hashing::blake2_128;
|
use sp_io::hashing::blake2_128;
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
|
||||||
use sp_weights::Weight;
|
use sp_weights::Weight;
|
||||||
use xcm::latest::prelude::*;
|
use xcm::latest::prelude::*;
|
||||||
|
|
||||||
@@ -199,10 +199,7 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
|
|||||||
let origin = origin.into();
|
let origin = origin.into();
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "xcm::execute_xcm_in_credit",
|
target: "xcm::execute_xcm_in_credit",
|
||||||
"origin: {:?}, message: {:?}, weight_credit: {:?}",
|
"origin: {origin:?}, message: {message:?}, weight_credit: {weight_credit:?}",
|
||||||
origin,
|
|
||||||
message,
|
|
||||||
weight_credit,
|
|
||||||
);
|
);
|
||||||
let mut properties = Properties { weight_credit, message_id: None };
|
let mut properties = Properties { weight_credit, message_id: None };
|
||||||
if let Err(e) = Config::Barrier::should_execute(
|
if let Err(e) = Config::Barrier::should_execute(
|
||||||
@@ -213,11 +210,8 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
|
|||||||
) {
|
) {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "xcm::execute_xcm_in_credit",
|
target: "xcm::execute_xcm_in_credit",
|
||||||
"Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, properties: {:?})",
|
"Barrier blocked execution! Error: {e:?}. \
|
||||||
e,
|
(origin: {origin:?}, message: {message:?}, properties: {properties:?})",
|
||||||
origin,
|
|
||||||
message,
|
|
||||||
properties,
|
|
||||||
);
|
);
|
||||||
return Outcome::Error(XcmError::Barrier)
|
return Outcome::Error(XcmError::Barrier)
|
||||||
}
|
}
|
||||||
@@ -228,7 +222,7 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
|
|||||||
|
|
||||||
while !message.0.is_empty() {
|
while !message.0.is_empty() {
|
||||||
let result = vm.process(message);
|
let result = vm.process(message);
|
||||||
log::trace!(target: "xcm::execute_xcm_in_credit", "result: {:?}", result);
|
log::trace!(target: "xcm::execute_xcm_in_credit", "result: {result:?}");
|
||||||
message = if let Err(error) = result {
|
message = if let Err(error) = result {
|
||||||
vm.total_surplus.saturating_accrue(error.weight);
|
vm.total_surplus.saturating_accrue(error.weight);
|
||||||
vm.error = Some((error.index, error.xcm_error));
|
vm.error = Some((error.index, error.xcm_error));
|
||||||
@@ -302,65 +296,6 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
|
||||||
pub fn bench_process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
|
|
||||||
self.process(xcm)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
|
|
||||||
log::trace!(
|
|
||||||
target: "xcm::process",
|
|
||||||
"origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}",
|
|
||||||
self.origin_ref(),
|
|
||||||
self.total_surplus,
|
|
||||||
self.total_refunded,
|
|
||||||
self.error_handler_weight,
|
|
||||||
);
|
|
||||||
let mut result = Ok(());
|
|
||||||
for (i, instr) in xcm.0.into_iter().enumerate() {
|
|
||||||
match &mut result {
|
|
||||||
r @ Ok(()) => {
|
|
||||||
// Initialize the recursion count only the first time we hit this code in our
|
|
||||||
// potential recursive execution.
|
|
||||||
let inst_res = recursion_count::using_once(&mut 1, || {
|
|
||||||
recursion_count::with(|count| {
|
|
||||||
if *count > RECURSION_LIMIT {
|
|
||||||
return Err(XcmError::ExceedsStackLimit)
|
|
||||||
}
|
|
||||||
*count = count.saturating_add(1);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
// This should always return `Some`, but let's play it safe.
|
|
||||||
.unwrap_or(Ok(()))?;
|
|
||||||
|
|
||||||
// Ensure that we always decrement the counter whenever we finish processing
|
|
||||||
// the instruction.
|
|
||||||
defer! {
|
|
||||||
recursion_count::with(|count| {
|
|
||||||
*count = count.saturating_sub(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.process_instruction(instr)
|
|
||||||
});
|
|
||||||
if let Err(e) = inst_res {
|
|
||||||
log::trace!(target: "xcm::execute", "!!! ERROR: {:?}", e);
|
|
||||||
*r = Err(ExecutorError {
|
|
||||||
index: i as u32,
|
|
||||||
xcm_error: e,
|
|
||||||
weight: Weight::zero(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(ref mut error) =>
|
|
||||||
if let Ok(x) = Config::Weigher::instr_weight(&instr) {
|
|
||||||
error.weight.saturating_accrue(x)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute any final operations after having executed the XCM message.
|
/// Execute any final operations after having executed the XCM message.
|
||||||
/// This includes refunding surplus weight, trapping extra holding funds, and returning any
|
/// This includes refunding surplus weight, trapping extra holding funds, and returning any
|
||||||
/// errors during execution.
|
/// errors during execution.
|
||||||
@@ -468,6 +403,154 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_fee(&mut self, fee: MultiAssets, reason: FeeReason) -> XcmResult {
|
||||||
|
if Config::FeeManager::is_waived(self.origin_ref(), reason) {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::fees",
|
||||||
|
"taking fee: {:?} from origin_ref: {:?} in fees_mode: {:?} for a reason: {:?}",
|
||||||
|
fee,
|
||||||
|
self.origin_ref(),
|
||||||
|
self.fees_mode,
|
||||||
|
reason,
|
||||||
|
);
|
||||||
|
let paid = if self.fees_mode.jit_withdraw {
|
||||||
|
let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
|
||||||
|
for asset in fee.inner() {
|
||||||
|
Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?;
|
||||||
|
}
|
||||||
|
fee
|
||||||
|
} else {
|
||||||
|
self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into()
|
||||||
|
};
|
||||||
|
Config::FeeManager::handle_fee(paid, Some(&self.context), reason);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates what `local_querier` would be from the perspective of `destination`.
|
||||||
|
fn to_querier(
|
||||||
|
local_querier: Option<MultiLocation>,
|
||||||
|
destination: &MultiLocation,
|
||||||
|
) -> Result<Option<MultiLocation>, XcmError> {
|
||||||
|
Ok(match local_querier {
|
||||||
|
None => None,
|
||||||
|
Some(q) => Some(
|
||||||
|
q.reanchored(&destination, Config::UniversalLocation::get())
|
||||||
|
.map_err(|_| XcmError::ReanchorFailed)?,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a bare `QueryResponse` message containing `response` informed by the given `info`.
|
||||||
|
///
|
||||||
|
/// The `local_querier` argument is the querier (if any) specified from the *local* perspective.
|
||||||
|
fn respond(
|
||||||
|
&mut self,
|
||||||
|
local_querier: Option<MultiLocation>,
|
||||||
|
response: Response,
|
||||||
|
info: QueryResponseInfo,
|
||||||
|
fee_reason: FeeReason,
|
||||||
|
) -> Result<XcmHash, XcmError> {
|
||||||
|
let querier = Self::to_querier(local_querier, &info.destination)?;
|
||||||
|
let QueryResponseInfo { destination, query_id, max_weight } = info;
|
||||||
|
let instruction = QueryResponse { query_id, response, max_weight, querier };
|
||||||
|
let message = Xcm(vec![instruction]);
|
||||||
|
self.send(destination, message, fee_reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_reanchor(
|
||||||
|
asset: MultiAsset,
|
||||||
|
destination: &MultiLocation,
|
||||||
|
) -> Result<(MultiAsset, InteriorMultiLocation), XcmError> {
|
||||||
|
let reanchor_context = Config::UniversalLocation::get();
|
||||||
|
let asset = asset
|
||||||
|
.reanchored(&destination, reanchor_context)
|
||||||
|
.map_err(|()| XcmError::ReanchorFailed)?;
|
||||||
|
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,
|
||||||
|
dest: &MultiLocation,
|
||||||
|
maybe_failed_bin: Option<&mut Assets>,
|
||||||
|
) -> MultiAssets {
|
||||||
|
let reanchor_context = Config::UniversalLocation::get();
|
||||||
|
assets.reanchor(dest, reanchor_context, maybe_failed_bin);
|
||||||
|
assets.into_assets_iter().collect::<Vec<_>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
pub fn bench_process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
|
||||||
|
self.process(xcm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process",
|
||||||
|
"origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}",
|
||||||
|
self.origin_ref(),
|
||||||
|
self.total_surplus,
|
||||||
|
self.total_refunded,
|
||||||
|
self.error_handler_weight,
|
||||||
|
);
|
||||||
|
let mut result = Ok(());
|
||||||
|
for (i, instr) in xcm.0.into_iter().enumerate() {
|
||||||
|
match &mut result {
|
||||||
|
r @ Ok(()) => {
|
||||||
|
// Initialize the recursion count only the first time we hit this code in our
|
||||||
|
// potential recursive execution.
|
||||||
|
let inst_res = recursion_count::using_once(&mut 1, || {
|
||||||
|
recursion_count::with(|count| {
|
||||||
|
if *count > RECURSION_LIMIT {
|
||||||
|
return Err(XcmError::ExceedsStackLimit)
|
||||||
|
}
|
||||||
|
*count = count.saturating_add(1);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
// This should always return `Some`, but let's play it safe.
|
||||||
|
.unwrap_or(Ok(()))?;
|
||||||
|
|
||||||
|
// Ensure that we always decrement the counter whenever we finish processing
|
||||||
|
// the instruction.
|
||||||
|
defer! {
|
||||||
|
recursion_count::with(|count| {
|
||||||
|
*count = count.saturating_sub(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.process_instruction(instr)
|
||||||
|
});
|
||||||
|
if let Err(e) = inst_res {
|
||||||
|
log::trace!(target: "xcm::execute", "!!! ERROR: {:?}", e);
|
||||||
|
*r = Err(ExecutorError {
|
||||||
|
index: i as u32,
|
||||||
|
xcm_error: e,
|
||||||
|
weight: Weight::zero(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(ref mut error) =>
|
||||||
|
if let Ok(x) = Config::Weigher::instr_weight(&instr) {
|
||||||
|
error.weight.saturating_accrue(x)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
/// Process a single XCM instruction, mutating the state of the XCM virtual machine.
|
/// Process a single XCM instruction, mutating the state of the XCM virtual machine.
|
||||||
fn process_instruction(
|
fn process_instruction(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -551,22 +634,81 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
},
|
},
|
||||||
Transact { origin_kind, require_weight_at_most, mut call } => {
|
Transact { origin_kind, require_weight_at_most, mut call } => {
|
||||||
// We assume that the Relay-chain is allowed to use transact on this parachain.
|
// We assume that the Relay-chain is allowed to use transact on this parachain.
|
||||||
let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?;
|
let origin = *self.origin_ref().ok_or_else(|| {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"No origin provided",
|
||||||
|
);
|
||||||
|
|
||||||
|
XcmError::BadOrigin
|
||||||
|
})?;
|
||||||
|
|
||||||
// TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain
|
// TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain
|
||||||
let message_call = call.take_decoded().map_err(|_| XcmError::FailedToDecode)?;
|
let message_call = call.take_decoded().map_err(|_| {
|
||||||
ensure!(Config::SafeCallFilter::contains(&message_call), XcmError::NoPermission);
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Failed to decode call",
|
||||||
|
);
|
||||||
|
|
||||||
|
XcmError::FailedToDecode
|
||||||
|
})?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Processing call: {message_call:?}",
|
||||||
|
);
|
||||||
|
|
||||||
|
if !Config::SafeCallFilter::contains(&message_call) {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Call filtered by `SafeCallFilter`",
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(XcmError::NoPermission)
|
||||||
|
}
|
||||||
|
|
||||||
let dispatch_origin = Config::OriginConverter::convert_origin(origin, origin_kind)
|
let dispatch_origin = Config::OriginConverter::convert_origin(origin, origin_kind)
|
||||||
.map_err(|_| XcmError::BadOrigin)?;
|
.map_err(|_| {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Failed to convert origin {origin:?} and origin kind {origin_kind:?} to a local origin."
|
||||||
|
);
|
||||||
|
|
||||||
|
XcmError::BadOrigin
|
||||||
|
})?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Dispatching with origin: {dispatch_origin:?}",
|
||||||
|
);
|
||||||
|
|
||||||
let weight = message_call.get_dispatch_info().weight;
|
let weight = message_call.get_dispatch_info().weight;
|
||||||
ensure!(weight.all_lte(require_weight_at_most), XcmError::MaxWeightInvalid);
|
|
||||||
|
if !weight.all_lte(require_weight_at_most) {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Max {weight} bigger than require at most {require_weight_at_most}",
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(XcmError::MaxWeightInvalid)
|
||||||
|
}
|
||||||
|
|
||||||
let maybe_actual_weight =
|
let maybe_actual_weight =
|
||||||
match Config::CallDispatcher::dispatch(message_call, dispatch_origin) {
|
match Config::CallDispatcher::dispatch(message_call, dispatch_origin) {
|
||||||
Ok(post_info) => {
|
Ok(post_info) => {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Dispatch successful: {post_info:?}"
|
||||||
|
);
|
||||||
self.transact_status = MaybeErrorCode::Success;
|
self.transact_status = MaybeErrorCode::Success;
|
||||||
post_info.actual_weight
|
post_info.actual_weight
|
||||||
},
|
},
|
||||||
Err(error_and_info) => {
|
Err(error_and_info) => {
|
||||||
|
log::trace!(
|
||||||
|
target: "xcm::process_instruction::transact",
|
||||||
|
"Dispatch failed {error_and_info:?}"
|
||||||
|
);
|
||||||
|
|
||||||
self.transact_status = error_and_info.error.encode().into();
|
self.transact_status = error_and_info.error.encode().into();
|
||||||
error_and_info.post_info.actual_weight
|
error_and_info.post_info.actual_weight
|
||||||
},
|
},
|
||||||
@@ -946,93 +1088,4 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
HrmpChannelClosing { .. } => Err(XcmError::Unimplemented),
|
HrmpChannelClosing { .. } => Err(XcmError::Unimplemented),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_fee(&mut self, fee: MultiAssets, reason: FeeReason) -> XcmResult {
|
|
||||||
if Config::FeeManager::is_waived(self.origin_ref(), reason) {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
log::trace!(
|
|
||||||
target: "xcm::fees",
|
|
||||||
"taking fee: {:?} from origin_ref: {:?} in fees_mode: {:?} for a reason: {:?}",
|
|
||||||
fee,
|
|
||||||
self.origin_ref(),
|
|
||||||
self.fees_mode,
|
|
||||||
reason,
|
|
||||||
);
|
|
||||||
let paid = if self.fees_mode.jit_withdraw {
|
|
||||||
let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
|
|
||||||
for asset in fee.inner() {
|
|
||||||
Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?;
|
|
||||||
}
|
|
||||||
fee
|
|
||||||
} else {
|
|
||||||
self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into()
|
|
||||||
};
|
|
||||||
Config::FeeManager::handle_fee(paid, Some(&self.context), reason);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates what `local_querier` would be from the perspective of `destination`.
|
|
||||||
fn to_querier(
|
|
||||||
local_querier: Option<MultiLocation>,
|
|
||||||
destination: &MultiLocation,
|
|
||||||
) -> Result<Option<MultiLocation>, XcmError> {
|
|
||||||
Ok(match local_querier {
|
|
||||||
None => None,
|
|
||||||
Some(q) => Some(
|
|
||||||
q.reanchored(&destination, Config::UniversalLocation::get())
|
|
||||||
.map_err(|_| XcmError::ReanchorFailed)?,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a bare `QueryResponse` message containing `response` informed by the given `info`.
|
|
||||||
///
|
|
||||||
/// The `local_querier` argument is the querier (if any) specified from the *local* perspective.
|
|
||||||
fn respond(
|
|
||||||
&mut self,
|
|
||||||
local_querier: Option<MultiLocation>,
|
|
||||||
response: Response,
|
|
||||||
info: QueryResponseInfo,
|
|
||||||
fee_reason: FeeReason,
|
|
||||||
) -> Result<XcmHash, XcmError> {
|
|
||||||
let querier = Self::to_querier(local_querier, &info.destination)?;
|
|
||||||
let QueryResponseInfo { destination, query_id, max_weight } = info;
|
|
||||||
let instruction = QueryResponse { query_id, response, max_weight, querier };
|
|
||||||
let message = Xcm(vec![instruction]);
|
|
||||||
self.send(destination, message, fee_reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_reanchor(
|
|
||||||
asset: MultiAsset,
|
|
||||||
destination: &MultiLocation,
|
|
||||||
) -> Result<(MultiAsset, InteriorMultiLocation), XcmError> {
|
|
||||||
let reanchor_context = Config::UniversalLocation::get();
|
|
||||||
let asset = asset
|
|
||||||
.reanchored(&destination, reanchor_context)
|
|
||||||
.map_err(|()| XcmError::ReanchorFailed)?;
|
|
||||||
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,
|
|
||||||
dest: &MultiLocation,
|
|
||||||
maybe_failed_bin: Option<&mut Assets>,
|
|
||||||
) -> MultiAssets {
|
|
||||||
let reanchor_context = Config::UniversalLocation::get();
|
|
||||||
assets.reanchor(dest, reanchor_context, maybe_failed_bin);
|
|
||||||
assets.into_assets_iter().collect::<Vec<_>>().into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
title: Improve XCM debuggability
|
||||||
|
|
||||||
|
doc:
|
||||||
|
- audience: Runtime User
|
||||||
|
description: |
|
||||||
|
Adds more logging to XCM execution to improve its debuggability.
|
||||||
|
|
||||||
|
crates:
|
||||||
|
- name: "staging-xcm-builder"
|
||||||
|
- name: "staging-xcm-executor"
|
||||||
@@ -1438,7 +1438,7 @@ pub trait Dispatchable {
|
|||||||
/// Every function call from your runtime has an origin, which specifies where the extrinsic was
|
/// Every function call from your runtime has an origin, which specifies where the extrinsic was
|
||||||
/// generated from. In the case of a signed extrinsic (transaction), the origin contains an
|
/// generated from. In the case of a signed extrinsic (transaction), the origin contains an
|
||||||
/// identifier for the caller. The origin can be empty in the case of an inherent extrinsic.
|
/// identifier for the caller. The origin can be empty in the case of an inherent extrinsic.
|
||||||
type RuntimeOrigin;
|
type RuntimeOrigin: Debug;
|
||||||
/// ...
|
/// ...
|
||||||
type Config;
|
type Config;
|
||||||
/// An opaque set of information attached to the transaction. This could be constructed anywhere
|
/// An opaque set of information attached to the transaction. This could be constructed anywhere
|
||||||
|
|||||||
Reference in New Issue
Block a user