Skip to main content

pop_chains/registry/
mod.rs

1// SPDX-License-Identifier: GPL-3.0
2
3use crate::{
4	traits::{
5		Args, Binary, Chain as ChainT, ChainSpec, GenesisOverrides, Id, Node, Override, Port,
6	},
7	up::Relay,
8};
9pub use pop::*;
10use pop_common::sourcing::traits::Source;
11use std::{
12	any::{Any, TypeId},
13	collections::HashMap,
14	sync::OnceLock,
15};
16pub use system::*;
17
18// Macro for reducing boilerplate code.
19macro_rules! impl_chain {
20	($name:ty) => {
21		impl AsChain for $name {
22			fn as_chain(&self) -> &Chain {
23				&self.0
24			}
25
26			fn as_chain_mut(&mut self) -> &mut Chain {
27				&mut self.0
28			}
29		}
30
31		impl traits::Chain for $name {
32			fn as_any(&self) -> &dyn std::any::Any {
33				self
34			}
35		}
36	};
37}
38
39mod pop;
40mod system;
41
42pub(crate) type ChainTypeId = TypeId;
43type Registry = HashMap<Relay, Vec<Box<dyn traits::Chain>>>;
44
45const REGISTRAR: fn(Registry) -> Registry = |mut registry| {
46	use Relay::*;
47	registry.insert(
48		Kusama,
49		vec![
50			// System chains
51			AssetHub::new(1_000, Kusama).into(),
52			BridgeHub::new(1_002, Kusama).into(),
53			Coretime::new(1_004, Kusama).into(),
54			People::new(1_005, Kusama).into(),
55		],
56	);
57	registry.insert(
58		Paseo,
59		vec![
60			// System chains
61			AssetHub::new(1_000, Paseo).into(),
62			Collectives::new(1_001, Paseo).into(),
63			BridgeHub::new(1_002, Paseo).into(),
64			Coretime::new(1_004, Paseo).into(),
65			People::new(1_005, Paseo).into(),
66			PassetHub::new(1_111, Paseo).into(),
67			// Others
68			Pop::new(4_001, "pop-devnet-local").into(),
69		],
70	);
71	registry.insert(
72		Polkadot,
73		vec![
74			// System chains
75			AssetHub::new(1_000, Polkadot).into(),
76			Collectives::new(1_001, Polkadot).into(),
77			BridgeHub::new(1_002, Polkadot).into(),
78			Coretime::new(1_004, Polkadot).into(),
79			People::new(1_005, Polkadot).into(),
80			// Others
81			Pop::new(3_395, "pop-local").into(),
82		],
83	);
84	registry.insert(
85		Westend,
86		vec![
87			// System chains
88			AssetHub::new(1_000, Westend).into(),
89			Collectives::new(1_001, Westend).into(),
90			BridgeHub::new(1_002, Westend).into(),
91			Coretime::new(1_004, Westend).into(),
92			People::new(1_005, Westend).into(),
93		],
94	);
95	registry
96};
97
98/// Returns the chains registered for the provided relay chain.
99///
100/// # Arguments
101/// * `relay` - The relay chain.
102pub fn chains(relay: &Relay) -> &'static [Box<dyn traits::Chain>] {
103	static REGISTRY: OnceLock<HashMap<Relay, Vec<Box<dyn traits::Chain>>>> = OnceLock::new();
104	static EMPTY: Vec<Box<dyn traits::Chain>> = Vec::new();
105
106	REGISTRY.get_or_init(|| REGISTRAR(HashMap::new())).get(relay).unwrap_or(&EMPTY)
107}
108
109// A base type, used by chain implementations to reduce boilerplate code.
110#[derive(Clone, Debug, PartialEq)]
111struct Chain {
112	name: String,
113	id: Id,
114	chain: String,
115	port: Option<Port>,
116}
117
118impl Chain {
119	fn new(name: impl Into<String>, id: Id, chain: impl Into<String>) -> Self {
120		Self { name: name.into(), id, chain: chain.into(), port: None }
121	}
122}
123
124impl ChainSpec for Chain {
125	fn chain(&self) -> &str {
126		self.chain.as_str()
127	}
128}
129
130impl ChainT for Chain {
131	fn id(&self) -> Id {
132		self.id
133	}
134
135	fn name(&self) -> &str {
136		self.name.as_str()
137	}
138
139	fn set_id(&mut self, id: Id) {
140		self.id = id;
141	}
142}
143
144impl Node for Chain {
145	fn port(&self) -> Option<&Port> {
146		self.port.as_ref()
147	}
148
149	fn set_port(&mut self, port: Port) {
150		self.port = Some(port);
151	}
152}
153
154trait AsChain {
155	fn as_chain(&self) -> &Chain;
156	fn as_chain_mut(&mut self) -> &mut Chain;
157}
158
159impl<T: AsChain + 'static> ChainSpec for T {
160	fn chain(&self) -> &str {
161		self.as_chain().chain()
162	}
163}
164
165impl<T: AsChain + 'static> ChainT for T {
166	fn id(&self) -> Id {
167		self.as_chain().id()
168	}
169
170	fn name(&self) -> &str {
171		self.as_chain().name()
172	}
173
174	fn set_id(&mut self, id: Id) {
175		self.as_chain_mut().set_id(id);
176	}
177}
178
179impl<T: AsChain + 'static> Node for T {
180	fn port(&self) -> Option<&Port> {
181		self.as_chain().port()
182	}
183
184	fn set_port(&mut self, port: Port) {
185		self.as_chain_mut().set_port(port);
186	}
187}
188
189impl<T: traits::Chain> From<T> for Box<dyn traits::Chain> {
190	fn from(value: T) -> Self {
191		Box::new(value)
192	}
193}
194
195/// Traits used by the chain registry.
196pub mod traits {
197	use super::*;
198
199	/// A meta-trait used specifically for trait objects.
200	pub trait Chain:
201		Any
202		+ Args
203		+ Binary
204		+ ChainSpec
205		+ GenesisOverrides
206		+ Node
207		+ Requires
208		+ crate::traits::Chain
209		+ ChainClone
210		+ Send
211		+ Source<Error = crate::Error>
212		+ Sync
213	{
214		/// Allows casting to [`Any`].
215		fn as_any(&self) -> &dyn Any;
216	}
217
218	/// A helper trait for ensuring [Chain] trait objects can be cloned.
219	pub trait ChainClone {
220		/// Returns a copy of the value.
221		fn clone_box(&self) -> Box<dyn Chain>;
222	}
223
224	impl<T: 'static + Chain + Clone> ChainClone for T {
225		fn clone_box(&self) -> Box<dyn Chain> {
226			Box::new(self.clone())
227		}
228	}
229
230	impl Clone for Box<dyn Chain> {
231		fn clone(&self) -> Self {
232			ChainClone::clone_box(self.as_ref())
233		}
234	}
235
236	/// The requirements of a chain.
237	pub trait Requires {
238		/// Defines the requirements of a chain, namely which other chain it depends on and any
239		/// corresponding overrides to genesis state.
240		fn requires(&self) -> Option<HashMap<ChainTypeId, Override>> {
241			None
242		}
243	}
244}
245
246#[cfg(test)]
247mod tests {
248	use super::*;
249	use crate::registry::traits::{Chain as _, Requires};
250	use Relay::*;
251
252	#[test]
253	fn chain_works() {
254		let mut chain = Chain::new("test-chain", 2_000, "test-local");
255		assert_eq!(chain.name(), "test-chain");
256		assert_eq!(chain.id(), 2_000);
257		assert_eq!(chain.chain(), "test-local");
258		assert!(chain.port().is_none());
259
260		chain.set_id(2_002);
261		assert_eq!(chain.id(), 2_002);
262
263		chain.set_port(9_944);
264		assert_eq!(chain.port().unwrap(), &9_944u16);
265	}
266
267	#[test]
268	fn impl_chain_works() {
269		let mut asset_hub = AssetHub::new(1_000, Paseo);
270		asset_hub.as_chain_mut().id = 1;
271		assert_eq!(asset_hub.id(), 1);
272		assert_eq!(asset_hub.as_chain(), &asset_hub.0);
273		assert_eq!(asset_hub.as_any().type_id(), asset_hub.type_id());
274		asset_hub.set_id(1_000);
275		assert_eq!(asset_hub.id(), 1_000);
276		asset_hub.set_port(9_944);
277		assert_eq!(asset_hub.port().unwrap(), &9_944u16);
278		assert!(asset_hub.requires().is_none());
279	}
280
281	#[test]
282	fn clone_works() {
283		let asset_hub: Box<dyn traits::Chain> = Box::new(AssetHub::new(1_000, Paseo));
284		assert_eq!(
285			asset_hub.clone().as_any().downcast_ref::<AssetHub>(),
286			asset_hub.as_any().downcast_ref::<AssetHub>()
287		);
288	}
289
290	#[test]
291	fn kusama_registry_works() {
292		let registry = chains(&Kusama);
293		assert!(contains::<AssetHub>(registry, 1_000));
294		assert!(contains::<BridgeHub>(registry, 1_002));
295		assert!(contains::<Coretime>(registry, 1_004));
296		assert!(contains::<People>(registry, 1_005));
297	}
298
299	#[test]
300	fn paseo_registry_works() {
301		let registry = chains(&Paseo);
302		assert!(contains::<AssetHub>(registry, 1_000));
303		assert!(contains::<Collectives>(registry, 1_001));
304		assert!(contains::<BridgeHub>(registry, 1_002));
305		assert!(contains::<Coretime>(registry, 1_004));
306		assert!(contains::<People>(registry, 1_005));
307		assert!(contains::<PassetHub>(registry, 1_111));
308		assert!(contains::<Pop>(registry, 4_001));
309	}
310
311	#[test]
312	fn polkadot_registry_works() {
313		let registry = chains(&Polkadot);
314		assert!(contains::<AssetHub>(registry, 1_000));
315		assert!(contains::<Collectives>(registry, 1_001));
316		assert!(contains::<BridgeHub>(registry, 1_002));
317		assert!(contains::<Coretime>(registry, 1_004));
318		assert!(contains::<People>(registry, 1_005));
319		assert!(contains::<Pop>(registry, 3_395));
320	}
321
322	#[test]
323	fn westend_registry_works() {
324		let registry = chains(&Westend);
325		assert!(contains::<AssetHub>(registry, 1_000));
326		assert!(contains::<Collectives>(registry, 1_001));
327		assert!(contains::<BridgeHub>(registry, 1_002));
328		assert!(contains::<Coretime>(registry, 1_004));
329		assert!(contains::<People>(registry, 1_005));
330	}
331
332	#[test]
333	fn type_checks() {
334		use std::any::{Any, TypeId};
335		use traits::Chain;
336
337		let asset_hub = AssetHub::new(1_000, Paseo);
338		let bridge_hub = BridgeHub::new(1_002, Polkadot);
339
340		assert_ne!(asset_hub.type_id(), bridge_hub.type_id());
341
342		let asset_hub: Box<dyn Chain> = Box::new(asset_hub);
343		let bridge_hub: Box<dyn Chain> = Box::new(bridge_hub);
344
345		assert_ne!(asset_hub.as_any().type_id(), bridge_hub.as_any().type_id());
346
347		assert_eq!(asset_hub.as_any().type_id(), TypeId::of::<AssetHub>());
348		assert_eq!(bridge_hub.as_any().type_id(), TypeId::of::<BridgeHub>());
349	}
350
351	fn contains<T: 'static>(registry: &[Box<dyn traits::Chain>], id: Id) -> bool {
352		registry
353			.iter()
354			.any(|r| r.as_any().type_id() == TypeId::of::<T>() && r.id() == id)
355	}
356}