Unverified Commit a543821d authored by Shawn Tabrizi's avatar Shawn Tabrizi Committed by GitHub
Browse files

Introduce XCM Weight Traits (#3802)



* introduce xcm weight traits

* patch some low hanging fruit

* add weightinfobound

* use checked math rather than saturating

* Update xcm/xcm-builder/src/weight.rs
Co-authored-by: default avatarKian Paimani <5588131+kianenigma@users.noreply.github.com>

* Revert "Update xcm/xcm-builder/src/weight.rs"

This reverts commit 6331b874

.
Co-authored-by: default avatarKian Paimani <5588131+kianenigma@users.noreply.github.com>
parent ba68ec70
Pipeline #156036 passed with stages
in 39 minutes and 12 seconds
......@@ -11577,6 +11577,7 @@ version = "0.9.9"
dependencies = [
"frame-support",
"frame-system",
"log",
"pallet-balances",
"pallet-transaction-payment",
"pallet-xcm",
......
......@@ -422,7 +422,7 @@ impl WrapVersion for AlwaysV1 {
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it.
/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before wrapping it.
pub struct AlwaysV2;
impl WrapVersion for AlwaysV2 {
fn wrap_version<Call>(
......@@ -474,3 +474,8 @@ pub mod opaque {
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
pub type VersionedXcm = super::VersionedXcm<()>;
}
// A simple trait to get the weight of some object.
pub trait GetWeight<W> {
fn weight(&self) -> latest::Weight;
}
......@@ -17,7 +17,7 @@
//! Version 1 of the Cross-Consensus Message format data structures.
use super::v1::{Order as OldOrder, Response as OldResponse, Xcm as OldXcm};
use crate::DoubleEncoded;
use crate::{DoubleEncoded, GetWeight};
use alloc::{vec, vec::Vec};
use core::{
convert::{TryFrom, TryInto},
......@@ -29,7 +29,9 @@ use parity_scale_codec::{self, Decode, Encode};
mod traits;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm};
pub use traits::{
Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo,
};
// These parts of XCM v1 have been unchanged in XCM v2, and are re-imported here.
pub use super::v1::{
Ancestor, AncestorThen, AssetId, AssetInstance, BodyId, BodyPart, Fungibility,
......@@ -127,7 +129,7 @@ pub mod prelude {
WeightLimit::{self, *},
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
WildMultiAsset::{self, *},
VERSION as XCM_VERSION,
XcmWeightInfo, VERSION as XCM_VERSION,
};
}
pub use super::{Instruction, Xcm};
......@@ -670,6 +672,54 @@ impl<Call> Instruction<Call> {
}
}
// TODO: Automate Generation
impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
fn weight(&self) -> Weight {
use Instruction::*;
match self {
WithdrawAsset(assets) => W::withdraw_asset(assets),
ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
QueryResponse { query_id, response, max_weight } =>
W::query_response(query_id, response, max_weight),
TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
TransferReserveAsset { assets, dest, xcm } =>
W::transfer_reserve_asset(&assets, dest, xcm),
Transact { origin_type, require_weight_at_most, call } =>
W::transact(origin_type, require_weight_at_most, call),
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
HrmpChannelClosing { initiator, sender, recipient } =>
W::hrmp_channel_closing(initiator, sender, recipient),
ClearOrigin => W::clear_origin(),
DescendOrigin(who) => W::descend_origin(who),
ReportError { query_id, dest, max_response_weight } =>
W::report_error(query_id, dest, max_response_weight),
DepositAsset { assets, max_assets, beneficiary } =>
W::deposit_asset(assets, max_assets, beneficiary),
DepositReserveAsset { assets, max_assets, dest, xcm } =>
W::deposit_reserve_asset(assets, max_assets, dest, xcm),
ExchangeAsset { give, receive } => W::exchange_asset(give, receive),
InitiateReserveWithdraw { assets, reserve, xcm } =>
W::initiate_reserve_withdraw(assets, reserve, xcm),
InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
QueryHolding { query_id, dest, assets, max_response_weight } =>
W::query_holding(query_id, dest, assets, max_response_weight),
BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
RefundSurplus => W::refund_surplus(),
SetErrorHandler(xcm) => W::set_error_handler(xcm),
SetAppendix(xcm) => W::set_appendix(xcm),
ClearError => W::clear_error(),
ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
Trap(code) => W::trap(code),
SubscribeVersion { query_id, max_response_weight } =>
W::subscribe_version(query_id, max_response_weight),
UnsubscribeVersion => W::unsubscribe_version(),
}
}
}
pub mod opaque {
/// The basic concrete type of `Xcm`, which doesn't make any assumptions about the
/// format of a call other than it is pre-encoded.
......
......@@ -19,7 +19,7 @@
use core::result;
use parity_scale_codec::{Decode, Encode};
use super::{MultiLocation, Xcm};
use super::*;
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug)]
pub enum Error {
......@@ -313,3 +313,63 @@ impl SendXcm for Tuple {
Err(SendError::CannotReachDestination(destination, message))
}
}
/// The info needed to weight an XCM.
// TODO: Automate Generation
pub trait XcmWeightInfo<Call> {
fn withdraw_asset(assets: &MultiAssets) -> Weight;
fn reserve_asset_deposited(assets: &MultiAssets) -> Weight;
fn receive_teleported_asset(assets: &MultiAssets) -> Weight;
fn query_response(query_id: &u64, response: &Response, max_weight: &u64) -> Weight;
fn transfer_asset(assets: &MultiAssets, beneficiary: &MultiLocation) -> Weight;
fn transfer_reserve_asset(assets: &MultiAssets, dest: &MultiLocation, xcm: &Xcm<()>) -> Weight;
fn transact(
origin_type: &OriginKind,
require_weight_at_most: &u64,
call: &DoubleEncoded<Call>,
) -> Weight;
fn hrmp_new_channel_open_request(
sender: &u32,
max_message_size: &u32,
max_capacity: &u32,
) -> Weight;
fn hrmp_channel_accepted(recipient: &u32) -> Weight;
fn hrmp_channel_closing(initiator: &u32, sender: &u32, recipient: &u32) -> Weight;
fn clear_origin() -> Weight;
fn descend_origin(who: &InteriorMultiLocation) -> Weight;
fn report_error(query_id: &QueryId, dest: &MultiLocation, max_response_weight: &u64) -> Weight;
fn relayed_from(who: &Junctions, message: &alloc::boxed::Box<Xcm<Call>>) -> Weight;
fn deposit_asset(
assets: &MultiAssetFilter,
max_assets: &u32,
beneficiary: &MultiLocation,
) -> Weight;
fn deposit_reserve_asset(
assets: &MultiAssetFilter,
max_assets: &u32,
dest: &MultiLocation,
xcm: &Xcm<()>,
) -> Weight;
fn exchange_asset(give: &MultiAssetFilter, receive: &MultiAssets) -> Weight;
fn initiate_reserve_withdraw(
assets: &MultiAssetFilter,
reserve: &MultiLocation,
xcm: &Xcm<()>,
) -> Weight;
fn initiate_teleport(assets: &MultiAssetFilter, dest: &MultiLocation, xcm: &Xcm<()>) -> Weight;
fn query_holding(
query_id: &u64,
dest: &MultiLocation,
assets: &MultiAssetFilter,
max_response_weight: &u64,
) -> Weight;
fn buy_execution(fees: &MultiAsset, weight_limit: &WeightLimit) -> Weight;
fn refund_surplus() -> Weight;
fn set_error_handler(xcm: &Xcm<Call>) -> Weight;
fn set_appendix(xcm: &Xcm<Call>) -> Weight;
fn clear_error() -> Weight;
fn claim_asset(assets: &MultiAssets, ticket: &MultiLocation) -> Weight;
fn trap(code: &u64) -> Weight;
fn subscribe_version(query_id: &QueryId, max_response_weight: &u64) -> Weight;
fn unsubscribe_version() -> Weight;
}
......@@ -16,6 +16,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
log = { version = "0.4.0", default-features = false }
# Polkadot dependencies
polkadot-parachain = { path = "../../parachain", default-features = false }
......@@ -29,6 +30,7 @@ polkadot-runtime-parachains = { path = "../../runtime/parachains" }
default = ["std"]
runtime-benchmarks = []
std = [
"log/std",
"parity-scale-codec/std",
"xcm/std",
"xcm-executor/std",
......
......@@ -142,6 +142,7 @@ impl<
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
log::trace!("xcm::currency_adapter deposit_asset {:?} {:?}", what, who);
// Check we handle this asset.
let amount: u128 =
Matcher::matches_fungible(&what).ok_or(Error::AssetNotFound)?.saturated_into();
......
......@@ -35,29 +35,81 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M: Get<u32>> WeightBounds<C>
let mut instructions_left = M::get();
Self::weight_with_limit(message, &mut instructions_left)
}
fn instr_weight(message: &Instruction<C>) -> Result<Weight, ()> {
Self::instr_weight_with_limit(message, &mut u32::max_value())
fn instr_weight(instruction: &Instruction<C>) -> Result<Weight, ()> {
Self::instr_weight_with_limit(instruction, &mut u32::max_value())
}
}
impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M> FixedWeightBounds<T, C, M> {
fn weight_with_limit(message: &Xcm<C>, instrs_limit: &mut u32) -> Result<Weight, ()> {
let mut r = 0;
let mut r: Weight = 0;
*instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?;
for m in message.0.iter() {
r += Self::instr_weight_with_limit(m, instrs_limit)?;
r = r.checked_add(Self::instr_weight_with_limit(m, instrs_limit)?).ok_or(())?;
}
Ok(r)
}
fn instr_weight_with_limit(
message: &Instruction<C>,
instruction: &Instruction<C>,
instrs_limit: &mut u32,
) -> Result<Weight, ()> {
Ok(T::get().saturating_add(match message {
Transact { require_weight_at_most, .. } => *require_weight_at_most,
SetErrorHandler(xcm) | SetAppendix(xcm) => Self::weight_with_limit(xcm, instrs_limit)?,
_ => 0,
}))
T::get()
.checked_add(match instruction {
Transact { require_weight_at_most, .. } => *require_weight_at_most,
SetErrorHandler(xcm) | SetAppendix(xcm) =>
Self::weight_with_limit(xcm, instrs_limit)?,
_ => 0,
})
.ok_or(())
}
}
struct WeightInfoBounds<W, C, M>(PhantomData<(W, C, M)>);
impl<W, C, M> WeightBounds<C> for WeightInfoBounds<W, C, M>
where
W: XcmWeightInfo<C>,
C: Decode + GetDispatchInfo,
M: Get<u32>,
Instruction<C>: xcm::GetWeight<W>,
{
fn weight(message: &mut Xcm<C>) -> Result<Weight, ()> {
let mut instructions_left = M::get();
Self::weight_with_limit(message, &mut instructions_left)
}
fn instr_weight(instruction: &Instruction<C>) -> Result<Weight, ()> {
Self::instr_weight_with_limit(instruction, &mut u32::max_value())
}
}
impl<W, C, M> WeightInfoBounds<W, C, M>
where
W: XcmWeightInfo<C>,
C: Decode + GetDispatchInfo,
M: Get<u32>,
Instruction<C>: xcm::GetWeight<W>,
{
fn weight_with_limit(message: &Xcm<C>, instrs_limit: &mut u32) -> Result<Weight, ()> {
let mut r: Weight = 0;
*instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?;
for m in message.0.iter() {
r = r.checked_add(Self::instr_weight_with_limit(m, instrs_limit)?).ok_or(())?;
}
Ok(r)
}
fn instr_weight_with_limit(
instruction: &Instruction<C>,
instrs_limit: &mut u32,
) -> Result<Weight, ()> {
use xcm::GetWeight;
instruction
.weight()
.checked_add(match instruction {
Transact { require_weight_at_most, .. } => *require_weight_at_most,
SetErrorHandler(xcm) | SetAppendix(xcm) =>
Self::weight_with_limit(xcm, instrs_limit)?,
_ => 0,
})
.ok_or(())
}
}
......
......@@ -130,9 +130,9 @@ impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
#[derive(Debug)]
pub struct ExecutorError {
index: u32,
xcm_error: XcmError,
weight: u64,
pub index: u32,
pub xcm_error: XcmError,
pub weight: u64,
}
#[cfg(feature = "runtime-benchmarks")]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment