Skip to content

Commit

Permalink
Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2 (#4978)
Browse files Browse the repository at this point in the history
It was added to v4 and v3 but was missing from v2
  • Loading branch information
franciscoaguirre authored Jul 9, 2024
1 parent 2f0e5a6 commit 9403a5d
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 2 deletions.
67 changes: 65 additions & 2 deletions polkadot/xcm/src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ use super::{
};
use alloc::{vec, vec::Vec};
use bounded_collections::{ConstU32, WeakBoundedVec};
use codec::{self, Decode, Encode, MaxEncodedLen};
use codec::{
self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput,
MaxEncodedLen,
};
use core::{fmt::Debug, result};
use derivative::Derivative;
use scale_info::TypeInfo;
Expand Down Expand Up @@ -278,14 +281,39 @@ pub const VERSION: super::Version = 2;
pub type QueryId = u64;

/// DEPRECATED. Please use XCMv3 or XCMv4 instead.
#[derive(Derivative, Default, Encode, Decode, TypeInfo)]
#[derive(Derivative, Default, Encode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub struct Xcm<RuntimeCall>(pub Vec<Instruction<RuntimeCall>>);

environmental::environmental!(instructions_count: u8);

impl<Call> Decode for Xcm<Call> {
fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
instructions_count::using_once(&mut 0, || {
let number_of_instructions: u32 = <Compact<u32>>::decode(input)?.into();
instructions_count::with(|count| {
*count = count.saturating_add(number_of_instructions as u8);
if *count > MAX_INSTRUCTIONS_TO_DECODE {
return Err(CodecError::from("Max instructions exceeded"))
}
Ok(())
})
.unwrap_or(Ok(()))?;
let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?;
Ok(Self(decoded_instructions))
})
}
}

/// The maximal number of instructions in an XCM before decoding fails.
///
/// This is a deliberate limit - not a technical one.
pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;

impl<RuntimeCall> Xcm<RuntimeCall> {
/// Create an empty instance.
pub fn new() -> Self {
Expand Down Expand Up @@ -1157,3 +1185,38 @@ impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCa
})
}
}

#[cfg(test)]
mod tests {
use super::{prelude::*, *};

#[test]
fn decoding_respects_limit() {
let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
let encoded = max_xcm.encode();
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());

let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
let encoded = big_xcm.encode();
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());

let nested_xcm = Xcm::<()>(vec![
DepositReserveAsset {
assets: All.into(),
dest: Here.into(),
xcm: max_xcm,
max_assets: 1,
};
(MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
]);
let encoded = nested_xcm.encode();
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());

let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
let encoded = even_more_nested_xcm.encode();
assert_eq!(encoded.len(), 345730);
// This should not decode since the limit is 100
assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
}
}
18 changes: 18 additions & 0 deletions prdoc/pr_4978.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Add MAX_INSTRUCTIONS_TO_DECODE to XCMv2

doc:
- audience: Runtime User
description: |
Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account.
It was set to 100.
- audience: Runtime Dev
description: |
Added a max number of instructions to XCMv2. If using XCMv2, you'll have to take this limit into account.
It was set to 100.

crates:
- name: staging-xcm
bump: minor

0 comments on commit 9403a5d

Please sign in to comment.