Skip to content

Commit cc4805b

Browse files
Add limits to XCMv4 (#3114)
Some more work regarding XCMv4. Two limits from v3 were not transferred over, those are: - The instructions limit - The number of assets limit Both of these are now in v4. For some reason `AssetInstance` increased in size, don't know why CI didn't catch that before. --------- Co-authored-by: Bastian Köcher <[email protected]> Co-authored-by: command-bot <>
1 parent 4450b61 commit cc4805b

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

polkadot/xcm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ fn size_limits() {
658658
(crate::latest::Junctions, 16),
659659
(crate::latest::Junction, 88),
660660
(crate::latest::Response, 40),
661-
(crate::latest::AssetInstance, 40),
661+
(crate::latest::AssetInstance, 48),
662662
(crate::latest::NetworkId, 48),
663663
(crate::latest::BodyId, 32),
664664
(crate::latest::Assets, 24),

polkadot/xcm/src/v3/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ pub const VERSION: super::Version = 3;
6969
/// An identifier for a query.
7070
pub type QueryId = u64;
7171

72-
// TODO (v4): Use `BoundedVec` instead of `Vec`
7372
#[derive(Derivative, Default, Encode, TypeInfo)]
7473
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
7574
#[codec(encode_bound())]

polkadot/xcm/src/v4/asset.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::v3::{
3333
WildFungibility as OldWildFungibility, WildMultiAsset as OldWildAsset,
3434
};
3535
use alloc::{vec, vec::Vec};
36+
use bounded_collections::{BoundedVec, ConstU32};
3637
use core::{
3738
cmp::Ordering,
3839
convert::{TryFrom, TryInto},
@@ -562,7 +563,9 @@ impl MaxEncodedLen for Assets {
562563

563564
impl Decode for Assets {
564565
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
565-
Self::from_sorted_and_deduplicated(Vec::<Asset>::decode(input)?)
566+
let bounded_instructions =
567+
BoundedVec::<Asset, ConstU32<{ MAX_ITEMS_IN_ASSETS as u32 }>>::decode(input)?;
568+
Self::from_sorted_and_deduplicated(bounded_instructions.into_inner())
566569
.map_err(|()| "Out of order".into())
567570
}
568571
}
@@ -1002,4 +1005,64 @@ mod tests {
10021005
let r = Assets::from_sorted_and_deduplicated(mixed_bad);
10031006
assert!(r.is_err());
10041007
}
1008+
1009+
#[test]
1010+
fn reanchor_preserves_sorting() {
1011+
use super::*;
1012+
use alloc::vec;
1013+
1014+
let reanchor_context: Junctions = Parachain(2000).into();
1015+
let dest = Location::new(1, []);
1016+
1017+
let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1018+
let mut asset_1_reanchored = asset_1.clone();
1019+
assert!(asset_1_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1020+
assert_eq!(
1021+
asset_1_reanchored,
1022+
(Location::new(0, [Parachain(2000), PalletInstance(50), GeneralIndex(1)]), 10).into()
1023+
);
1024+
1025+
let asset_2: Asset = (Location::new(1, []), 10).into();
1026+
let mut asset_2_reanchored = asset_2.clone();
1027+
assert!(asset_2_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1028+
assert_eq!(asset_2_reanchored, (Location::new(0, []), 10).into());
1029+
1030+
let asset_3: Asset = (Location::new(1, [Parachain(1000)]), 10).into();
1031+
let mut asset_3_reanchored = asset_3.clone();
1032+
assert!(asset_3_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1033+
assert_eq!(asset_3_reanchored, (Location::new(0, [Parachain(1000)]), 10).into());
1034+
1035+
let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
1036+
assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
1037+
1038+
assert!(assets.reanchor(&dest, &reanchor_context).is_ok());
1039+
assert_eq!(assets, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored].into());
1040+
}
1041+
1042+
#[test]
1043+
fn decoding_respects_limit() {
1044+
use super::*;
1045+
1046+
// Having lots of one asset will work since they are deduplicated
1047+
let lots_of_one_asset: Assets =
1048+
vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_ASSETS + 1].into();
1049+
let encoded = lots_of_one_asset.encode();
1050+
assert!(Assets::decode(&mut &encoded[..]).is_ok());
1051+
1052+
// Fewer assets than the limit works
1053+
let mut few_assets: Assets = Vec::new().into();
1054+
for i in 0..MAX_ITEMS_IN_ASSETS {
1055+
few_assets.push((GeneralIndex(i as u128), 1u128).into());
1056+
}
1057+
let encoded = few_assets.encode();
1058+
assert!(Assets::decode(&mut &encoded[..]).is_ok());
1059+
1060+
// Having lots of different assets will not work
1061+
let mut too_many_different_assets: Assets = Vec::new().into();
1062+
for i in 0..MAX_ITEMS_IN_ASSETS + 1 {
1063+
too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into());
1064+
}
1065+
let encoded = too_many_different_assets.encode();
1066+
assert!(Assets::decode(&mut &encoded[..]).is_err());
1067+
}
10051068
}

polkadot/xcm/src/v4/mod.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ use core::{
3030
result,
3131
};
3232
use derivative::Derivative;
33-
use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen};
33+
use parity_scale_codec::{
34+
self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput,
35+
MaxEncodedLen,
36+
};
3437
use scale_info::TypeInfo;
3538

3639
mod asset;
@@ -59,7 +62,7 @@ pub const VERSION: super::Version = 4;
5962
/// An identifier for a query.
6063
pub type QueryId = u64;
6164

62-
#[derive(Derivative, Default, Encode, Decode, TypeInfo)]
65+
#[derive(Derivative, Default, Encode, TypeInfo)]
6366
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
6467
#[codec(encode_bound())]
6568
#[codec(decode_bound())]
@@ -68,6 +71,26 @@ pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
6871

6972
pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;
7073

74+
environmental::environmental!(instructions_count: u8);
75+
76+
impl<Call> Decode for Xcm<Call> {
77+
fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
78+
instructions_count::using_once(&mut 0, || {
79+
let number_of_instructions: u32 = <Compact<u32>>::decode(input)?.into();
80+
instructions_count::with(|count| {
81+
*count = count.saturating_add(number_of_instructions as u8);
82+
if *count > MAX_INSTRUCTIONS_TO_DECODE {
83+
return Err(CodecError::from("Max instructions exceeded"))
84+
}
85+
Ok(())
86+
})
87+
.expect("Called in `using` context and thus can not return `None`; qed")?;
88+
let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?;
89+
Ok(Self(decoded_instructions))
90+
})
91+
}
92+
}
93+
7194
impl<Call> Xcm<Call> {
7295
/// Create an empty instance.
7396
pub fn new() -> Self {
@@ -1454,4 +1477,33 @@ mod tests {
14541477
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
14551478
assert_eq!(new_xcm, xcm);
14561479
}
1480+
1481+
#[test]
1482+
fn decoding_respects_limit() {
1483+
let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1484+
let encoded = max_xcm.encode();
1485+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1486+
1487+
let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1488+
let encoded = big_xcm.encode();
1489+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1490+
1491+
let nested_xcm = Xcm::<()>(vec![
1492+
DepositReserveAsset {
1493+
assets: All.into(),
1494+
dest: Here.into(),
1495+
xcm: max_xcm,
1496+
};
1497+
(MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1498+
]);
1499+
let encoded = nested_xcm.encode();
1500+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1501+
1502+
let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1503+
let encoded = even_more_nested_xcm.encode();
1504+
assert_eq!(encoded.len(), 342530);
1505+
// This should not decode since the limit is 100
1506+
assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1507+
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1508+
}
14571509
}

0 commit comments

Comments
 (0)