1use 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
18macro_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 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 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 Pop::new(4_001, "pop-devnet-local").into(),
69 ],
70 );
71 registry.insert(
72 Polkadot,
73 vec![
74 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 Pop::new(3_395, "pop-local").into(),
82 ],
83 );
84 registry.insert(
85 Westend,
86 vec![
87 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
98pub 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#[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
195pub mod traits {
197 use super::*;
198
199 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 fn as_any(&self) -> &dyn Any;
216 }
217
218 pub trait ChainClone {
220 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 pub trait Requires {
238 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}