1pub use pallet::*;
19
20use codec::Encode;
21
22pub const TEST_RUNTIME_UPGRADE_KEY: &[u8] = b"+test_runtime_upgrade_key+";
26
27pub fn relay_alice_account_key() -> alloc::vec::Vec<u8> {
29 use sp_keyring::Sr25519Keyring;
30
31 let alice = Sr25519Keyring::Alice.to_account_id();
32
33 let mut key = sp_io::hashing::twox_128(b"System").to_vec();
34 key.extend_from_slice(&sp_io::hashing::twox_128(b"Account"));
35 key.extend_from_slice(&sp_io::hashing::blake2_128(&alice.encode()));
36 key.extend_from_slice(&alice.encode());
37 key
38}
39
40#[frame_support::pallet(dev_mode)]
41pub mod pallet {
42 use crate::test_pallet::TEST_RUNTIME_UPGRADE_KEY;
43 use alloc::{vec, vec::Vec};
44 use cumulus_primitives_core::{CumulusDigestItem, ParaId, XcmpMessageSource};
45 use cumulus_primitives_storage_weight_reclaim::get_proof_size;
46 use frame_support::{
47 dispatch::DispatchInfo,
48 inherent::{InherentData, InherentIdentifier, ProvideInherent},
49 pallet_prelude::*,
50 traits::IsSubType,
51 weights::constants::WEIGHT_REF_TIME_PER_SECOND,
52 DebugNoBound,
53 };
54 use frame_system::pallet_prelude::*;
55 use sp_runtime::traits::{Dispatchable, Implication, TransactionExtension};
56
57 pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"consume0";
59
60 #[pallet::pallet]
61 pub struct Pallet<T>(_);
62
63 #[pallet::config]
64 pub trait Config: frame_system::Config + cumulus_pallet_parachain_system::Config {}
65
66 #[pallet::storage]
68 pub type TestMap<T: Config> = StorageMap<_, Twox64Concat, u32, (), ValueQuery>;
69
70 #[pallet::storage]
72 pub type PendingOutboundHrmpMessages<T: Config> =
73 StorageValue<_, alloc::vec::Vec<(ParaId, alloc::vec::Vec<u8>)>, ValueQuery>;
74
75 impl<T: Config> XcmpMessageSource for Pallet<T> {
76 fn take_outbound_messages(
77 maximum_channels: usize,
78 excluded_recipients: &[ParaId],
79 ) -> alloc::vec::Vec<(ParaId, alloc::vec::Vec<u8>)> {
80 PendingOutboundHrmpMessages::<T>::mutate(|messages| {
81 let mut taken_recipients = alloc::vec::Vec::new();
82 let mut result = alloc::vec::Vec::new();
83 messages.retain(|(recipient, data)| {
84 if result.len() >= maximum_channels ||
85 excluded_recipients.contains(recipient) ||
86 taken_recipients.contains(recipient)
87 {
88 return true;
89 }
90 taken_recipients.push(*recipient);
91 result.push((*recipient, data.clone()));
92 false
93 });
94 result
95 })
96 }
97 }
98
99 #[pallet::storage]
104 pub type HrmpSendingActive<T: Config> = StorageValue<_, bool, ValueQuery>;
105
106 #[pallet::storage]
108 pub type ScheduleWeightRegistration<T: Config> = StorageValue<_, bool, ValueQuery>;
109
110 #[pallet::storage]
112 pub type InherentWeightConsume<T: Config> = StorageValue<_, Weight, OptionQuery>;
113
114 #[pallet::storage]
120 pub type BigValueMove<T: Config> =
121 StorageMap<_, Twox64Concat, BlockNumberFor<T>, Vec<u8>, OptionQuery>;
122
123 pub const HRMP_RECIPIENT_LOW: u32 = 2500;
124 pub const HRMP_RECIPIENT_HIGH: u32 = 2600;
125
126 #[pallet::hooks]
127 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
128 fn on_initialize(n: BlockNumberFor<T>) -> Weight {
129 if HrmpSendingActive::<T>::get() {
130 let block_num: u32 = n.try_into().unwrap_or(0);
131 let recipient = if block_num % 2 == 1 {
132 ParaId::from(HRMP_RECIPIENT_HIGH)
133 } else {
134 ParaId::from(HRMP_RECIPIENT_LOW)
135 };
136 PendingOutboundHrmpMessages::<T>::mutate(|messages| {
137 messages.push((recipient, vec![block_num as u8]));
138 });
139 }
140
141 if ScheduleWeightRegistration::<T>::get() {
142 let weight_to_register = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0);
143
144 let left_weight = frame_system::Pallet::<T>::remaining_block_weight();
145
146 if left_weight.can_consume(weight_to_register) {
147 tracing::info!("Consuming 1s of weight :)");
148 ScheduleWeightRegistration::<T>::kill();
150 return weight_to_register;
151 }
152 }
153
154 if let Some(mut value) = BigValueMove::<T>::take(n - 1u32.into()) {
155 let parent_hash = frame_system::Pallet::<T>::parent_hash();
157 value[..parent_hash.as_ref().len()].copy_from_slice(parent_hash.as_ref());
158
159 BigValueMove::<T>::insert(n, value);
160
161 Self::deposit_event(Event::MovedBigValue {
165 proof_size: get_proof_size().unwrap_or_default(),
166 })
167 }
168
169 Weight::zero()
170 }
171 }
172
173 #[pallet::call]
174 impl<T: Config> Pallet<T> {
175 #[pallet::weight(0)]
177 pub fn set_custom_validation_head_data(
178 _: OriginFor<T>,
179 custom_header: alloc::vec::Vec<u8>,
180 ) -> DispatchResult {
181 cumulus_pallet_parachain_system::Pallet::<T>::set_custom_validation_head_data(
182 custom_header,
183 );
184 Ok(())
185 }
186
187 #[pallet::weight(0)]
191 pub fn read_and_write_child_tries(_: OriginFor<T>) -> DispatchResult {
192 let key = &b"hello"[..];
193 let first_trie = &b"first"[..];
194 let second_trie = &b"second"[..];
195 let first_value = "world1".encode();
196 let second_value = "world2".encode();
197
198 if let Some(res) = sp_io::default_child_storage::get(first_trie, key) {
199 assert_eq!(first_value, res);
200 }
201 if let Some(res) = sp_io::default_child_storage::get(second_trie, key) {
202 assert_eq!(second_value, res);
203 }
204
205 sp_io::default_child_storage::set(first_trie, key, &first_value);
206 sp_io::default_child_storage::set(second_trie, key, &second_value);
207
208 Ok(())
209 }
210
211 pub fn read_and_write_big_value(_: OriginFor<T>) -> DispatchResult {
215 let key = &b"really_huge_value"[..];
216 sp_io::storage::get(key);
217 sp_io::storage::set(key, &vec![0u8; 1024 * 1024 * 5]);
218
219 Ok(())
220 }
221
222 #[pallet::weight(0)]
224 pub fn store_values_in_map(_: OriginFor<T>, max_key: u32) -> DispatchResult {
225 for i in 0..=max_key {
226 TestMap::<T>::insert(i, ());
227 }
228 Ok(())
229 }
230
231 #[pallet::weight(0)]
233 pub fn remove_value_from_map(_: OriginFor<T>, key: u32) -> DispatchResult {
234 TestMap::<T>::remove(key);
235 Ok(())
236 }
237
238 #[pallet::weight(0)]
240 pub fn send_n_upward_messages(_: OriginFor<T>, n: u32) -> DispatchResult {
241 let messages: alloc::vec::Vec<_> = (0..n).map(|i| vec![(i % 256) as u8]).collect();
242 cumulus_pallet_parachain_system::PendingUpwardMessages::<T>::put(messages);
243 Ok(())
244 }
245
246 #[pallet::weight(0)]
248 pub fn send_upward_message_of_size(_: OriginFor<T>, size: u32) -> DispatchResult {
249 let message = alloc::vec![0u8; size as usize];
250 cumulus_pallet_parachain_system::Pallet::<T>::send_upward_message(message)
251 .map_err(|_| "Failed to send upward message")?;
252 Ok(())
253 }
254
255 #[pallet::weight(0)]
257 pub fn queue_hrmp_messages(_: OriginFor<T>, n: u32, recipient: ParaId) -> DispatchResult {
258 PendingOutboundHrmpMessages::<T>::mutate(|messages| {
259 for i in 0..n {
260 messages.push((recipient, vec![(i % 256) as u8]));
261 }
262 });
263 Ok(())
264 }
265
266 #[pallet::weight(0)]
269 pub fn queue_hrmp_messages_to_n_recipients(
270 _: OriginFor<T>,
271 n: u32,
272 first_recipient: ParaId,
273 ) -> DispatchResult {
274 PendingOutboundHrmpMessages::<T>::mutate(|messages| {
275 for i in 0..n {
276 messages.push((ParaId::from(u32::from(first_recipient) + i), vec![i as u8]));
277 }
278 });
279 Ok(())
280 }
281
282 #[pallet::weight(0)]
284 pub fn schedule_weight_registration(_: OriginFor<T>) -> DispatchResult {
285 ScheduleWeightRegistration::<T>::set(true);
286 Ok(())
287 }
288
289 #[pallet::weight(0)]
291 pub fn set_inherent_weight_consume(_: OriginFor<T>, weight: Weight) -> DispatchResult {
292 InherentWeightConsume::<T>::put(weight);
293 Ok(())
294 }
295
296 #[pallet::weight((
298 InherentWeightConsume::<T>::get().unwrap_or_default(),
299 DispatchClass::Mandatory
300 ))]
301 pub fn consume_weight_inherent(origin: OriginFor<T>) -> DispatchResult {
302 ensure_none(origin)?;
303
304 InherentWeightConsume::<T>::kill();
306
307 Ok(())
308 }
309
310 #[pallet::weight(0)]
316 pub fn use_more_weight_than_announced(
317 _: OriginFor<T>,
318 _must_be_first_block_in_core: bool,
319 ) -> DispatchResult {
320 frame_system::Pallet::<T>::register_extra_weight_unchecked(
322 Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0),
323 DispatchClass::Normal,
324 );
325
326 Ok(())
327 }
328
329 #[pallet::weight(0)]
332 pub fn set_use_full_core(_: OriginFor<T>) -> DispatchResult {
333 frame_system::Pallet::<T>::deposit_log(CumulusDigestItem::UseFullCore.to_digest_item());
334 Ok(())
335 }
336 }
337
338 #[pallet::inherent]
339 impl<T: Config> ProvideInherent for Pallet<T> {
340 type Call = Call<T>;
341 type Error = sp_inherents::MakeFatalError<()>;
342 const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
343
344 fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
345 let weight_to_consume = InherentWeightConsume::<T>::get()?;
347
348 let remaining_weight = frame_system::Pallet::<T>::remaining_block_weight();
350
351 if remaining_weight.can_consume(weight_to_consume) {
352 Some(Call::consume_weight_inherent {})
353 } else {
354 None
356 }
357 }
358
359 fn is_inherent(call: &Self::Call) -> bool {
360 matches!(call, Call::consume_weight_inherent {})
361 }
362 }
363
364 #[derive(frame_support::DefaultNoBound)]
365 #[pallet::genesis_config]
366 pub struct GenesisConfig<T: Config> {
367 #[serde(skip)]
368 pub _config: core::marker::PhantomData<T>,
369 pub enable_big_value_move: bool,
371 pub enable_hrmp_sending: bool,
373 }
374
375 #[pallet::genesis_build]
376 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
377 fn build(&self) {
378 sp_io::storage::set(TEST_RUNTIME_UPGRADE_KEY, &[1, 2, 3, 4]);
379
380 if self.enable_big_value_move {
381 BigValueMove::<T>::insert(BlockNumberFor::<T>::from(0u32), vec![0u8; 4 * 1024]);
382 }
383
384 if self.enable_hrmp_sending {
385 HrmpSendingActive::<T>::set(true);
386 }
387 }
388 }
389
390 #[pallet::event]
391 #[pallet::generate_deposit(pub(super) fn deposit_event)]
392 pub enum Event<T: Config> {
393 MovedBigValue { proof_size: u64 },
394 }
395
396 #[derive(
397 DebugNoBound,
398 Encode,
399 Decode,
400 CloneNoBound,
401 EqNoBound,
402 PartialEqNoBound,
403 TypeInfo,
404 DecodeWithMemTracking,
405 )]
406 #[scale_info(skip_type_params(T))]
407 pub struct TestTransactionExtension<T>(core::marker::PhantomData<T>);
408
409 impl<T> Default for TestTransactionExtension<T> {
410 fn default() -> Self {
411 Self(core::marker::PhantomData)
412 }
413 }
414
415 impl<T: Config> TransactionExtension<T::RuntimeCall> for TestTransactionExtension<T>
416 where
417 T: Config + Send + Sync,
418 T::RuntimeCall: IsSubType<Call<T>> + Dispatchable<Info = DispatchInfo>,
419 {
420 const IDENTIFIER: &'static str = "TestTransactionExtension";
421 type Implicit = ();
422 type Val = ();
423 type Pre = ();
424
425 fn validate(
426 &self,
427 origin: T::RuntimeOrigin,
428 call: &T::RuntimeCall,
429 _info: &DispatchInfo,
430 _len: usize,
431 _self_implicit: Self::Implicit,
432 _inherited_implication: &impl Implication,
433 _: TransactionSource,
434 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
435 if let Some(call) = call.is_sub_type() {
436 match call {
437 Call::use_more_weight_than_announced { must_be_first_block_in_core } => {
438 if {
439 let digest = frame_system::Pallet::<T>::digest();
440
441 CumulusDigestItem::find_block_bundle_info(&digest)
442 .map_or(true, |bi| {
444 bi.index == 0 && *must_be_first_block_in_core ||
447 bi.index > 0 && !*must_be_first_block_in_core
449 })
450 } {
451 Ok((
452 ValidTransaction {
453 provides: vec![vec![1, 2, 3, 4, 5]],
454 ..Default::default()
455 },
456 (),
457 origin,
458 ))
459 } else {
460 Err(TransactionValidityError::Invalid(
461 InvalidTransaction::ExhaustsResources,
462 ))
463 }
464 },
465 _ => Ok((Default::default(), (), origin)),
466 }
467 } else {
468 Ok((Default::default(), (), origin))
469 }
470 }
471
472 fn prepare(
473 self,
474 val: Self::Val,
475 _origin: &T::RuntimeOrigin,
476 _call: &T::RuntimeCall,
477 _info: &DispatchInfo,
478 _len: usize,
479 ) -> Result<Self::Pre, TransactionValidityError> {
480 Ok(val)
481 }
482
483 fn weight(&self, _: &T::RuntimeCall) -> Weight {
484 Weight::zero()
485 }
486 }
487}
488
489impl<T: Config> cumulus_pallet_parachain_system::OnSystemEvent for Pallet<T> {
490 fn on_validation_data(_data: &cumulus_primitives_core::PersistedValidationData) {
491 }
493
494 fn on_validation_code_applied() {
495 }
497
498 fn on_relay_state_proof(
499 relay_state_proof: &cumulus_pallet_parachain_system::relay_state_snapshot::RelayChainStateProof,
500 ) -> frame_support::weights::Weight {
501 use crate::{Balance, Nonce};
502 use frame_system::AccountInfo;
503 use pallet_balances::AccountData;
504
505 let alice_key = crate::test_pallet::relay_alice_account_key();
506
507 relay_state_proof
509 .read_optional_entry::<AccountInfo<Nonce, AccountData<Balance>>>(&alice_key)
510 .expect("Invalid relay chain state proof");
511
512 frame_support::weights::Weight::zero()
513 }
514}