1#[allow(unexpected_cfgs)]
19mod parachain;
20
21#[allow(unexpected_cfgs)]
23mod relay_chain;
24
25use codec::DecodeLimit;
26use polkadot_core_primitives::AccountId;
27use polkadot_parachain_primitives::primitives::Id as ParaId;
28use sp_runtime::{traits::AccountIdConversion, BuildStorage};
29use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt};
30
31#[cfg(feature = "try-runtime")]
32use frame_support::traits::{TryState, TryStateSelect::All};
33use frame_support::{assert_ok, traits::IntegrityTest};
34use xcm::{latest::prelude::*, MAX_XCM_DECODE_DEPTH};
35
36use arbitrary::{Arbitrary, Error, Unstructured};
37
38pub const INITIAL_BALANCE: u128 = 1_000_000_000;
39
40decl_test_parachain! {
41 pub struct ParaA {
42 Runtime = parachain::Runtime,
43 XcmpMessageHandler = parachain::MsgQueue,
44 DmpMessageHandler = parachain::MsgQueue,
45 new_ext = para_ext(1),
46 }
47}
48
49decl_test_parachain! {
50 pub struct ParaB {
51 Runtime = parachain::Runtime,
52 XcmpMessageHandler = parachain::MsgQueue,
53 DmpMessageHandler = parachain::MsgQueue,
54 new_ext = para_ext(2),
55 }
56}
57
58decl_test_parachain! {
59 pub struct ParaC {
60 Runtime = parachain::Runtime,
61 XcmpMessageHandler = parachain::MsgQueue,
62 DmpMessageHandler = parachain::MsgQueue,
63 new_ext = para_ext(3),
64 }
65}
66
67decl_test_relay_chain! {
68 pub struct Relay {
69 Runtime = relay_chain::Runtime,
70 RuntimeCall = relay_chain::RuntimeCall,
71 RuntimeEvent = relay_chain::RuntimeEvent,
72 XcmConfig = relay_chain::XcmConfig,
73 MessageQueue = relay_chain::MessageQueue,
74 System = relay_chain::System,
75 new_ext = relay_ext(),
76 }
77}
78
79decl_test_network! {
80 pub struct MockNet {
81 relay_chain = Relay,
82 parachains = vec![
83 (1, ParaA),
84 (2, ParaB),
85 (3, ParaC),
86 ],
87 }
88}
89
90struct XcmMessage {
92 source: u32,
94 destination: u32,
96 message: Xcm<()>,
98}
99
100impl<'a> Arbitrary<'a> for XcmMessage {
101 fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, Error> {
102 let source: u32 = u.arbitrary()?;
103 let destination: u32 = u.arbitrary()?;
104 let mut encoded_message: &[u8] = u.arbitrary()?;
105 if let Ok(message) =
106 DecodeLimit::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut encoded_message)
107 {
108 return Ok(XcmMessage { source, destination, message });
109 }
110 Err(Error::IncorrectFormat)
111 }
112}
113
114pub fn para_account_id(id: u32) -> relay_chain::AccountId {
115 ParaId::from(id).into_account_truncating()
116}
117
118pub fn para_ext(para_id: u32) -> sp_io::TestExternalities {
119 use parachain::{MsgQueue, Runtime, System};
120
121 let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
122
123 pallet_balances::GenesisConfig::<Runtime> {
124 balances: (0..6).map(|i| ([i; 32].into(), INITIAL_BALANCE)).collect(),
125 ..Default::default()
126 }
127 .assimilate_storage(&mut t)
128 .unwrap();
129
130 let mut ext = sp_io::TestExternalities::new(t);
131 ext.execute_with(|| {
132 System::set_block_number(1);
133 MsgQueue::set_para_id(para_id.into());
134 });
135 ext
136}
137
138pub fn relay_ext() -> sp_io::TestExternalities {
139 use relay_chain::{Runtime, System};
140
141 let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
142
143 let mut balances: Vec<(AccountId, u128)> = vec![];
144 balances.append(&mut (1..=3).map(|i| (para_account_id(i), INITIAL_BALANCE)).collect());
145 balances.append(&mut (0..6).map(|i| ([i; 32].into(), INITIAL_BALANCE)).collect());
146
147 pallet_balances::GenesisConfig::<Runtime> { balances, ..Default::default() }
148 .assimilate_storage(&mut t)
149 .unwrap();
150
151 let mut ext = sp_io::TestExternalities::new(t);
152 ext.execute_with(|| System::set_block_number(1));
153 ext
154}
155
156pub type RelayChainPalletXcm = pallet_xcm::Pallet<relay_chain::Runtime>;
157pub type ParachainPalletXcm = pallet_xcm::Pallet<parachain::Runtime>;
158
159fn recursively_matches_blocklisted_messages(message: &Instruction<()>) -> bool {
161 match message {
162 DepositReserveAsset { xcm, .. } |
163 ExportMessage { xcm, .. } |
164 InitiateReserveWithdraw { xcm, .. } |
165 InitiateTeleport { xcm, .. } |
166 TransferReserveAsset { xcm, .. } |
167 SetErrorHandler(xcm) |
168 SetAppendix(xcm) => xcm.iter().any(recursively_matches_blocklisted_messages),
169 m => matches!(m, Transact { .. }),
171 }
172}
173
174fn run_input(xcm_messages: [XcmMessage; 5]) {
175 MockNet::reset();
176
177 #[cfg(not(fuzzing))]
178 println!();
179
180 for xcm_message in xcm_messages {
181 if xcm_message.message.iter().any(recursively_matches_blocklisted_messages) {
182 println!(" skipping message\n");
183 continue;
184 }
185
186 if xcm_message.source % 4 == 0 {
187 let parachain_id = (xcm_message.destination % 3) + 1;
189 let destination: Location = Parachain(parachain_id).into();
190 #[cfg(not(fuzzing))]
191 {
192 println!(" source: Relay Chain");
193 println!(" destination: Parachain {parachain_id}");
194 println!(" message: {:?}", xcm_message.message);
195 }
196 Relay::execute_with(|| {
197 assert_ok!(RelayChainPalletXcm::send_xcm(Here, destination, xcm_message.message));
198 })
199 } else {
200 let execute_with = match xcm_message.source % 4 {
202 1 => ParaA::execute_with,
203 2 => ParaB::execute_with,
204 _ => ParaC::execute_with,
205 };
206 let destination: Location = match xcm_message.destination % 4 {
208 n @ 1..=3 => (Parent, Parachain(n)).into(),
209 _ => Parent.into(),
210 };
211 #[cfg(not(fuzzing))]
212 {
213 let destination_str = match xcm_message.destination % 4 {
214 n @ 1..=3 => format!("Parachain {n}"),
215 _ => "Relay Chain".to_string(),
216 };
217 println!(" source: Parachain {}", xcm_message.source % 4);
218 println!(" destination: {}", destination_str);
219 println!(" message: {:?}", xcm_message.message);
220 }
221 execute_with(|| {
223 assert_ok!(ParachainPalletXcm::send_xcm(Here, destination, xcm_message.message));
224 });
225 }
226 #[cfg(not(fuzzing))]
227 println!();
228 [ParaA::execute_with, ParaB::execute_with, ParaC::execute_with].iter().for_each(
230 |execute_with| {
231 execute_with(|| {
232 #[cfg(feature = "try-runtime")]
233 parachain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap();
234 parachain::AllPalletsWithSystem::integrity_test();
235 });
236 },
237 );
238 Relay::execute_with(|| {
239 #[cfg(feature = "try-runtime")]
240 relay_chain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap();
241 relay_chain::AllPalletsWithSystem::integrity_test();
242 });
243 }
244}
245
246fn main() {
247 #[cfg(fuzzing)]
248 {
249 loop {
250 honggfuzz::fuzz!(|xcm_messages: [XcmMessage; 5]| {
251 run_input(xcm_messages);
252 })
253 }
254 }
255 #[cfg(not(fuzzing))]
256 {
257 use std::{env, fs, fs::File, io::Read};
258 let args: Vec<_> = env::args().collect();
259 let md = fs::metadata(&args[1]).unwrap();
260 let all_files = match md.is_dir() {
261 true => fs::read_dir(&args[1])
262 .unwrap()
263 .map(|x| x.unwrap().path().to_str().unwrap().to_string())
264 .collect::<Vec<String>>(),
265 false => (args[1..]).to_vec(),
266 };
267 println!("All_files {:?}", all_files);
268 for argument in all_files {
269 println!("Now doing file {:?}", argument);
270 let mut buffer: Vec<u8> = Vec::new();
271 let mut f = File::open(argument).unwrap();
272 f.read_to_end(&mut buffer).unwrap();
273 let mut unstructured = Unstructured::new(&buffer);
274 if let Ok(xcm_messages) = unstructured.arbitrary() {
275 run_input(xcm_messages);
276 }
277 }
278 }
279}