Contracts: xcm host fn fixes (#3086)

## Xcm changes:
- Fix `pallet_xcm::execute`, move the logic into The `ExecuteController`
so it can be shared with anything that implement that trait.
- Make `ExecuteController::execute` retursn `DispatchErrorWithPostInfo`
instead of `DispatchError`, so that we don't charge the full
`max_weight` provided if the execution is incomplete (useful for
force_batch or contracts calls)
- Fix docstring for `pallet_xcm::execute`, to reflect the changes from
#2405
- Update the signature for `ExecuteController::execute`, we don't need
to return the `Outcome` anymore since we only care about
`Outcome::Complete`

## Contracts changes:

- Update host fn `xcm_exexute`, we don't need to write the `Outcome` to
the sandbox memory anymore. This was also not charged as well before so
it if fixes this too.
- One of the issue was that the dry_run of a contract that call
`xcm_execute` would exhaust the `gas_limit`.

This is because `XcmExecuteController::execute` takes a `max_weight`
argument, and since we don't want the user to specify it manually we
were passing everything left by pre-charghing
`ctx.ext.gas_meter().gas_left()`

- To fix it I added a `fn influence_lowest_limit` on the `Token` trait
and make it return false for `RuntimeCost::XcmExecute`.
- Got rid of the `RuntimeToken` indirection, we can just use
`RuntimeCost` directly.

---------

Co-authored-by: command-bot <>
This commit is contained in:
PG Herveou
2024-02-19 16:29:47 +01:00
committed by GitHub
parent 320863a847
commit ca382f3203
13 changed files with 192 additions and 133 deletions
+34 -26
View File
@@ -29,7 +29,7 @@ pub mod migration;
use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
use frame_support::{
dispatch::GetDispatchInfo,
dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo, WithPostDispatchInfo},
pallet_prelude::*,
traits::{
Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency,
@@ -291,22 +291,38 @@ pub mod pallet {
origin: OriginFor<T>,
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
max_weight: Weight,
) -> Result<Outcome, DispatchError> {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let value = (origin_location, message);
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, message) = value;
let outcome = T::XcmExecutor::prepare_and_execute(
origin_location,
message,
&mut hash,
max_weight,
max_weight,
);
) -> Result<Weight, DispatchErrorWithPostInfo> {
log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
let outcome = (|| {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let value = (origin_location, message);
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, message) = value;
Ok(T::XcmExecutor::prepare_and_execute(
origin_location,
message,
&mut hash,
max_weight,
max_weight,
))
})()
.map_err(|e: DispatchError| {
e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
})?;
Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
Ok(outcome)
let weight_used = outcome.weight_used();
outcome.ensure_complete().map_err(|error| {
log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
Error::<T>::LocalExecutionIncomplete.with_weight(
weight_used.saturating_add(
<Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
),
)
})?;
Ok(weight_used)
}
}
@@ -1009,9 +1025,6 @@ pub mod pallet {
/// No more than `max_weight` will be used in its attempted execution. If this is less than
/// the maximum amount of weight that the message could take to be executed, then no
/// execution attempt will be made.
///
/// NOTE: A successful return to this does *not* imply that the `msg` was executed
/// successfully to completion; only that it was attempted.
#[pallet::call_index(3)]
#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
pub fn execute(
@@ -1019,13 +1032,8 @@ pub mod pallet {
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
max_weight: Weight,
) -> DispatchResultWithPostInfo {
log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
let outcome = <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
let weight_used = outcome.weight_used();
outcome.ensure_complete().map_err(|error| {
log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
Error::<T>::LocalExecutionIncomplete
})?;
let weight_used =
<Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
}
+21 -17
View File
@@ -20,11 +20,12 @@ pub(crate) mod assets_transfer;
use crate::{
mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error,
LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue,
VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo,
ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus,
VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets,
WeightInfo,
};
use frame_support::{
assert_noop, assert_ok,
assert_err_ignore_postinfo, assert_noop, assert_ok,
traits::{Currency, Hooks},
weights::Weight,
};
@@ -450,19 +451,19 @@ fn trapped_assets_can_be_claimed() {
assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE + SEND_AMOUNT);
assert_eq!(AssetTraps::<Test>::iter().collect::<Vec<_>>(), vec![]);
let weight = BaseXcmWeight::get() * 3;
assert_ok!(<XcmPallet as xcm_builder::ExecuteController<_, _>>::execute(
RuntimeOrigin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]))),
weight
));
let outcome =
Outcome::Incomplete { used: BaseXcmWeight::get(), error: XcmError::UnknownClaim };
assert_eq!(last_event(), RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome }));
// Can't claim twice.
assert_err_ignore_postinfo!(
XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]))),
weight
),
Error::<Test>::LocalExecutionIncomplete
);
});
}
@@ -495,11 +496,14 @@ fn incomplete_execute_reverts_side_effects() {
// all effects are reverted and balances unchanged for either sender or receiver
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE);
assert_eq!(
result,
Err(sp_runtime::DispatchErrorWithPostInfo {
post_info: frame_support::dispatch::PostDispatchInfo {
actual_weight: None,
actual_weight: Some(
<Pallet<Test> as ExecuteControllerWeightInfo>::execute() + weight
),
pays_fee: frame_support::dispatch::Pays::Yes,
},
error: sp_runtime::DispatchError::Module(sp_runtime::ModuleError {