1use codec::Decode;
18use core::{marker::PhantomData, result::Result};
19use frame_support::{
20 dispatch::GetDispatchInfo,
21 traits::{
22 fungible::{Balanced, Credit, Inspect},
23 Get, OnUnbalanced as OnUnbalancedT,
24 },
25 weights::{
26 constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND},
27 WeightToFee as WeightToFeeT,
28 },
29};
30use sp_runtime::traits::{SaturatedConversion, Saturating, Zero};
31use xcm::latest::{prelude::*, GetWeight, Weight};
32use xcm_executor::{
33 traits::{WeightBounds, WeightTrader},
34 AssetsInHolding,
35};
36
37pub struct FixedWeightBounds<T, C, M>(PhantomData<(T, C, M)>);
38impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M: Get<u32>> WeightBounds<C>
39 for FixedWeightBounds<T, C, M>
40{
41 fn weight(message: &mut Xcm<C>, weight_limit: Weight) -> Result<Weight, InstructionError> {
42 tracing::trace!(target: "xcm::weight", ?message, "FixedWeightBounds");
43 let mut instructions_left = M::get();
44 Self::weight_with_limit(message, &mut instructions_left, weight_limit).inspect_err(
45 |&error| {
46 tracing::debug!(
47 target: "xcm::weight",
48 ?error,
49 ?instructions_left,
50 message_length = ?message.0.len(),
51 "Weight calculation failed for message"
52 );
53 },
54 )
55 }
56 fn instr_weight(instruction: &mut Instruction<C>) -> Result<Weight, XcmError> {
57 let mut max_value = u32::MAX;
58 Self::instr_weight_with_limit(instruction, &mut max_value, Weight::MAX).inspect_err(
59 |&error| {
60 tracing::debug!(
61 target: "xcm::weight",
62 ?error,
63 ?instruction,
64 instrs_limit = ?max_value,
65 "Weight calculation failed for instruction"
66 );
67 },
68 )
69 }
70}
71
72impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M> FixedWeightBounds<T, C, M> {
73 fn weight_with_limit(
74 message: &mut Xcm<C>,
75 instructions_left: &mut u32,
76 weight_limit: Weight,
77 ) -> Result<Weight, InstructionError> {
78 let mut total_weight: Weight = Weight::zero();
79 for (index, instruction) in message.0.iter_mut().enumerate() {
80 let index = index.try_into().unwrap_or(InstructionIndex::MAX);
81 *instructions_left = instructions_left
82 .checked_sub(1)
83 .ok_or_else(|| InstructionError { index, error: XcmError::ExceedsStackLimit })?;
84 let instruction_weight =
85 &Self::instr_weight_with_limit(instruction, instructions_left, weight_limit)
86 .map_err(|error| InstructionError { index, error })?;
87 total_weight = total_weight
88 .checked_add(instruction_weight)
89 .ok_or(InstructionError { index, error: XcmError::Overflow })?;
90 if total_weight.any_gt(weight_limit) {
91 return Err(InstructionError {
92 index,
93 error: XcmError::WeightLimitReached(total_weight),
94 });
95 }
96 }
97 Ok(total_weight)
98 }
99
100 fn instr_weight_with_limit(
101 instruction: &mut Instruction<C>,
102 instructions_left: &mut u32,
103 weight_limit: Weight,
104 ) -> Result<Weight, XcmError> {
105 let instruction_weight = match instruction {
106 Transact { ref mut call, .. } =>
107 call.ensure_decoded()
108 .map_err(|_| XcmError::FailedToDecode)?
109 .get_dispatch_info()
110 .call_weight,
111 SetErrorHandler(xcm) | SetAppendix(xcm) | ExecuteWithOrigin { xcm, .. } =>
112 Self::weight_with_limit(xcm, instructions_left, weight_limit)
113 .map_err(|outcome_error| outcome_error.error)?,
114 _ => Weight::zero(),
115 };
116 let total_weight = T::get().checked_add(&instruction_weight).ok_or(XcmError::Overflow)?;
117 Ok(total_weight)
118 }
119}
120
121pub struct WeightInfoBounds<W, C, M>(PhantomData<(W, C, M)>);
122impl<W, C, M> WeightBounds<C> for WeightInfoBounds<W, C, M>
123where
124 W: XcmWeightInfo<C>,
125 C: Decode + GetDispatchInfo,
126 M: Get<u32>,
127 Instruction<C>: xcm::latest::GetWeight<W>,
128{
129 fn weight(message: &mut Xcm<C>, weight_limit: Weight) -> Result<Weight, InstructionError> {
130 tracing::trace!(target: "xcm::weight", ?message, "WeightInfoBounds");
131 let mut instructions_left = M::get();
132 Self::weight_with_limit(message, &mut instructions_left, weight_limit).inspect_err(
133 |&error| {
134 tracing::debug!(
135 target: "xcm::weight",
136 ?error,
137 ?instructions_left,
138 message_length = ?message.0.len(),
139 "Weight calculation failed for message"
140 );
141 },
142 )
143 }
144 fn instr_weight(instruction: &mut Instruction<C>) -> Result<Weight, XcmError> {
145 let mut max_value = u32::MAX;
146 Self::instr_weight_with_limit(instruction, &mut max_value, Weight::MAX).inspect_err(
147 |&error| {
148 tracing::debug!(
149 target: "xcm::weight",
150 ?error,
151 ?instruction,
152 instrs_limit = ?max_value,
153 "Weight calculation failed for instruction"
154 );
155 },
156 )
157 }
158}
159
160impl<W, C, M> WeightInfoBounds<W, C, M>
161where
162 W: XcmWeightInfo<C>,
163 C: Decode + GetDispatchInfo,
164 M: Get<u32>,
165 Instruction<C>: xcm::latest::GetWeight<W>,
166{
167 fn weight_with_limit(
168 message: &mut Xcm<C>,
169 instructions_left: &mut u32,
170 weight_limit: Weight,
171 ) -> Result<Weight, InstructionError> {
172 let mut total_weight: Weight = Weight::zero();
173 for (index, instruction) in message.0.iter_mut().enumerate() {
174 let index = index.try_into().unwrap_or(u8::MAX);
175 *instructions_left = instructions_left
176 .checked_sub(1)
177 .ok_or_else(|| InstructionError { index, error: XcmError::ExceedsStackLimit })?;
178 let instruction_weight =
179 &Self::instr_weight_with_limit(instruction, instructions_left, weight_limit)
180 .map_err(|error| InstructionError { index, error })?;
181 total_weight = total_weight
182 .checked_add(instruction_weight)
183 .ok_or(InstructionError { index, error: XcmError::Overflow })?;
184 if total_weight.any_gt(weight_limit) {
185 return Err(InstructionError {
186 index,
187 error: XcmError::WeightLimitReached(total_weight),
188 });
189 }
190 }
191 Ok(total_weight)
192 }
193
194 fn instr_weight_with_limit(
195 instruction: &mut Instruction<C>,
196 instructions_left: &mut u32,
197 weight_limit: Weight,
198 ) -> Result<Weight, XcmError> {
199 let instruction_weight = match instruction {
200 Transact { ref mut call, .. } =>
201 call.ensure_decoded()
202 .map_err(|_| XcmError::FailedToDecode)?
203 .get_dispatch_info()
204 .call_weight,
205 SetErrorHandler(xcm) | SetAppendix(xcm) =>
206 Self::weight_with_limit(xcm, instructions_left, weight_limit)
207 .map_err(|outcome_error| outcome_error.error)?,
208 _ => Weight::zero(),
209 };
210 let total_weight = instruction
211 .weight()
212 .checked_add(&instruction_weight)
213 .ok_or(XcmError::Overflow)?;
214 Ok(total_weight)
215 }
216}
217
218pub trait TakeRevenue {
222 fn take_revenue(revenue: Asset);
224}
225
226impl TakeRevenue for () {
228 fn take_revenue(_revenue: Asset) {}
229}
230
231pub struct FixedRateOfFungible<T: Get<(AssetId, u128, u128)>, R: TakeRevenue>(
236 Weight,
237 u128,
238 PhantomData<(T, R)>,
239);
240impl<T: Get<(AssetId, u128, u128)>, R: TakeRevenue> WeightTrader for FixedRateOfFungible<T, R> {
241 fn new() -> Self {
242 Self(Weight::zero(), 0, PhantomData)
243 }
244
245 fn buy_weight(
246 &mut self,
247 weight: Weight,
248 payment: AssetsInHolding,
249 context: &XcmContext,
250 ) -> Result<AssetsInHolding, XcmError> {
251 let (id, units_per_second, units_per_mb) = T::get();
252 tracing::trace!(
253 target: "xcm::weight",
254 ?id, ?weight, ?payment, ?context,
255 "FixedRateOfFungible::buy_weight",
256 );
257 let amount = (units_per_second * (weight.ref_time() as u128) /
258 (WEIGHT_REF_TIME_PER_SECOND as u128)) +
259 (units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128));
260 if amount == 0 {
261 return Ok(payment)
262 }
263 let unused = payment.checked_sub((id, amount).into()).map_err(|error| {
264 tracing::error!(target: "xcm::weight", ?amount, ?error, "FixedRateOfFungible::buy_weight Failed to substract from payment");
265 XcmError::TooExpensive
266 })?;
267 self.0 = self.0.saturating_add(weight);
268 self.1 = self.1.saturating_add(amount);
269 Ok(unused)
270 }
271
272 fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option<Asset> {
273 let (id, units_per_second, units_per_mb) = T::get();
274 tracing::trace!(target: "xcm::weight", ?id, ?weight, ?context, "FixedRateOfFungible::refund_weight");
275 let weight = weight.min(self.0);
276 let amount = (units_per_second * (weight.ref_time() as u128) /
277 (WEIGHT_REF_TIME_PER_SECOND as u128)) +
278 (units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128));
279 self.0 -= weight;
280 self.1 = self.1.saturating_sub(amount);
281 if amount > 0 {
282 Some((id, amount).into())
283 } else {
284 None
285 }
286 }
287}
288
289impl<T: Get<(AssetId, u128, u128)>, R: TakeRevenue> Drop for FixedRateOfFungible<T, R> {
290 fn drop(&mut self) {
291 if self.1 > 0 {
292 R::take_revenue((T::get().0, self.1).into());
293 }
294 }
295}
296
297pub struct UsingComponents<
300 WeightToFee: WeightToFeeT<Balance = <Fungible as Inspect<AccountId>>::Balance>,
301 AssetIdValue: Get<Location>,
302 AccountId,
303 Fungible: Balanced<AccountId> + Inspect<AccountId>,
304 OnUnbalanced: OnUnbalancedT<Credit<AccountId, Fungible>>,
305>(
306 Weight,
307 Fungible::Balance,
308 PhantomData<(WeightToFee, AssetIdValue, AccountId, Fungible, OnUnbalanced)>,
309);
310impl<
311 WeightToFee: WeightToFeeT<Balance = <Fungible as Inspect<AccountId>>::Balance>,
312 AssetIdValue: Get<Location>,
313 AccountId,
314 Fungible: Balanced<AccountId> + Inspect<AccountId>,
315 OnUnbalanced: OnUnbalancedT<Credit<AccountId, Fungible>>,
316 > WeightTrader for UsingComponents<WeightToFee, AssetIdValue, AccountId, Fungible, OnUnbalanced>
317{
318 fn new() -> Self {
319 Self(Weight::zero(), Zero::zero(), PhantomData)
320 }
321
322 fn buy_weight(
323 &mut self,
324 weight: Weight,
325 payment: AssetsInHolding,
326 context: &XcmContext,
327 ) -> Result<AssetsInHolding, XcmError> {
328 tracing::trace!(target: "xcm::weight", ?weight, ?payment, ?context, "UsingComponents::buy_weight");
329 let amount = WeightToFee::weight_to_fee(&weight);
330 let u128_amount: u128 = amount.try_into().map_err(|_| {
331 tracing::debug!(target: "xcm::weight", ?amount, "Weight fee could not be converted");
332 XcmError::Overflow
333 })?;
334 let required = Asset { id: AssetId(AssetIdValue::get()), fun: Fungible(u128_amount) };
335 let unused = payment.checked_sub(required).map_err(|error| {
336 tracing::debug!(target: "xcm::weight", ?error, "Failed to substract from payment");
337 XcmError::TooExpensive
338 })?;
339 self.0 = self.0.saturating_add(weight);
340 self.1 = self.1.saturating_add(amount);
341 Ok(unused)
342 }
343
344 fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option<Asset> {
345 tracing::trace!(target: "xcm::weight", ?weight, ?context, available_weight = ?self.0, available_amount = ?self.1, "UsingComponents::refund_weight");
346 let weight = weight.min(self.0);
347 let amount = WeightToFee::weight_to_fee(&weight);
348 self.0 -= weight;
349 self.1 = self.1.saturating_sub(amount);
350 let amount: u128 = amount.saturated_into();
351 tracing::trace!(target: "xcm::weight", ?amount, "UsingComponents::refund_weight");
352 if amount > 0 {
353 Some((AssetIdValue::get(), amount).into())
354 } else {
355 None
356 }
357 }
358}
359impl<
360 WeightToFee: WeightToFeeT<Balance = <Fungible as Inspect<AccountId>>::Balance>,
361 AssetId: Get<Location>,
362 AccountId,
363 Fungible: Balanced<AccountId> + Inspect<AccountId>,
364 OnUnbalanced: OnUnbalancedT<Credit<AccountId, Fungible>>,
365 > Drop for UsingComponents<WeightToFee, AssetId, AccountId, Fungible, OnUnbalanced>
366{
367 fn drop(&mut self) {
368 OnUnbalanced::on_unbalanced(Fungible::issue(self.1));
369 }
370}