Skip to main content

pop_chains/
relay.rs

1// SPDX-License-Identifier: GPL-3.0
2
3use crate::{DynamicPayload, Error, call};
4use scale::{Decode, Encode};
5use sp_core::twox_128;
6use subxt::{
7	OnlineClient,
8	dynamic::{self, Value},
9	events::StaticEvent,
10	ext::{scale_decode::DecodeAsType, scale_encode::EncodeAsType},
11	utils::H256,
12};
13
14/// Clears the DMPQ state for the given parachain IDs.
15///
16/// # Arguments
17/// * `client` - Client for the network which state is to be modified.
18/// * `para_ids` - List of ids to build the keys that will be mutated.
19pub async fn clear_dmpq(endpoint: &str, para_ids: &[u32]) -> Result<H256, Error> {
20	let client = OnlineClient::<subxt::PolkadotConfig>::from_url(endpoint)
21		.await
22		.map_err(|e| Error::SubXtError(e.into()))?;
23	// Wait for blocks to be produced.
24	let mut sub = client
25		.blocks()
26		.subscribe_finalized()
27		.await
28		.map_err(|e| Error::SubXtError(e.into()))?;
29	for _ in 0..2 {
30		sub.next().await;
31	}
32
33	// Generate storage keys to be removed
34	let clear_dmq_keys = generate_storage_keys(para_ids);
35
36	// Submit calls to remove specified keys
37	let kill_storage = construct_kill_storage_call(clear_dmq_keys);
38	let sudo = subxt_signer::sr25519::dev::alice();
39	let sudo_call = call::construct_sudo_extrinsic(kill_storage);
40	client
41		.tx()
42		.sign_and_submit_default(&sudo_call, &sudo)
43		.await
44		.map_err(|e| Error::SubXtError(e.into()))
45}
46
47fn construct_kill_storage_call(keys: Vec<Vec<u8>>) -> DynamicPayload {
48	dynamic::tx(
49		"System",
50		"kill_storage",
51		vec![Value::unnamed_composite(keys.into_iter().map(Value::from_bytes))],
52	)
53}
54
55fn generate_storage_keys(para_ids: &[u32]) -> Vec<Vec<u8>> {
56	let dmp = twox_128("Dmp".as_bytes());
57	let dmp_queue_heads = twox_128("DownwardMessageQueueHeads".as_bytes());
58	let dmp_queues = twox_128("DownwardMessageQueues".as_bytes());
59	let mut clear_dmq_keys = Vec::<Vec<u8>>::new();
60	for id in para_ids {
61		let id = id.to_le_bytes();
62		// DMP Queue Head
63		let mut key = dmp.to_vec();
64		key.extend(&dmp_queue_heads);
65		key.extend(sp_core::twox_64(&id));
66		key.extend(id);
67		clear_dmq_keys.push(key);
68		// DMP Queue
69		let mut key = dmp.to_vec();
70		key.extend(&dmp_queues);
71		key.extend(sp_core::twox_64(&id));
72		key.extend(id);
73		clear_dmq_keys.push(key);
74	}
75	clear_dmq_keys
76}
77
78/// A supported relay chain.
79#[derive(Debug, PartialEq)]
80pub enum RelayChain {
81	/// Paseo.
82	PaseoLocal,
83	/// Westend.
84	WestendLocal,
85}
86
87impl RelayChain {
88	/// Attempts to convert a chain identifier into a supported `RelayChain` variant.
89	///
90	/// # Arguments
91	/// * `id` - The relay chain identifier.
92	pub fn from(id: &str) -> Option<RelayChain> {
93		match id {
94			"paseo-local" => Some(RelayChain::PaseoLocal),
95			"westend-local" => Some(RelayChain::WestendLocal),
96			_ => None,
97		}
98	}
99}
100
101/// A event emitted when an id has been registered.
102#[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)]
103#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
104#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
105pub struct Reserved {
106	/// The id that has been reserved.
107	pub para_id: u32,
108}
109impl StaticEvent for Reserved {
110	const PALLET: &'static str = "Registrar";
111	const EVENT: &'static str = "Reserved";
112}
113
114#[cfg(test)]
115mod tests {
116	use super::*;
117	use RelayChain::*;
118	use sp_core::twox_64;
119
120	#[test]
121	fn construct_kill_storage_call_works() {
122		let keys = vec!["key".as_bytes().to_vec()];
123		assert_eq!(
124			construct_kill_storage_call(keys.clone()),
125			dynamic::tx(
126				"System",
127				"kill_storage",
128				vec![Value::unnamed_composite(keys.into_iter().map(Value::from_bytes))],
129			)
130		)
131	}
132
133	#[test]
134	fn generate_storage_keys_works() {
135		let para_ids = vec![1_000, 4_385];
136		let dmp = twox_128("Dmp".as_bytes());
137		let dmp_queue_heads = [dmp, twox_128("DownwardMessageQueueHeads".as_bytes())].concat();
138		let dmp_queues = [dmp, twox_128("DownwardMessageQueues".as_bytes())].concat();
139
140		assert_eq!(
141			generate_storage_keys(&para_ids),
142			para_ids
143				.iter()
144				.flat_map(|id| {
145					let id = id.to_le_bytes().to_vec();
146					[
147						// DMP Queue Head
148						[dmp_queue_heads.clone(), twox_64(&id).to_vec(), id.clone()].concat(),
149						// DMP Queue
150						[dmp_queues.clone(), twox_64(&id).to_vec(), id].concat(),
151					]
152				})
153				.collect::<Vec<_>>()
154		)
155	}
156
157	#[test]
158	fn supported_relay_chains() {
159		for (s, e) in [
160			// Only chains with sudo supported
161			("paseo-local", Some(PaseoLocal)),
162			("westend-local", Some(WestendLocal)),
163			("kusama-local", None),
164			("polkadot-local", None),
165		] {
166			assert_eq!(RelayChain::from(s), e)
167		}
168	}
169}