1
// Copyright (C) Parity Technologies (UK) Ltd.
2
// This file is part of Polkadot.
3

            
4
// Polkadot is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Polkadot is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
18

            
19
use super::{Junctions, MultiLocation};
20
use crate::{
21
	v2::{
22
		BodyId as OldBodyId, BodyPart as OldBodyPart, Junction as OldJunction,
23
		NetworkId as OldNetworkId,
24
	},
25
	v4::{Junction as NewJunction, NetworkId as NewNetworkId},
26
	VersionedLocation,
27
};
28
use bounded_collections::{BoundedSlice, BoundedVec, ConstU32};
29
use codec::{self, Decode, Encode, MaxEncodedLen};
30
use scale_info::TypeInfo;
31
use serde::{Deserialize, Serialize};
32

            
33
/// A global identifier of a data structure existing within consensus.
34
///
35
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
36
/// Polkadot ecosystem are given preference over explicit naming in this enumeration.
37
#[derive(
38
	Copy,
39
	Clone,
40
	Eq,
41
	PartialEq,
42
	Ord,
43
	PartialOrd,
44
	Encode,
45
27
	Decode,
46
	Debug,
47
	TypeInfo,
48
	MaxEncodedLen,
49
	Serialize,
50
	Deserialize,
51
)]
52
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
53
#[scale_info(replace_segment("staging_xcm", "xcm"))]
54
pub enum NetworkId {
55
2103
	/// Network specified by the first 32 bytes of its genesis block.
56
2103
	ByGenesis([u8; 32]),
57
693
	/// Network defined by the first 32-bytes of the hash and number of some block it contains.
58
693
	ByFork { block_number: u64, block_hash: [u8; 32] },
59
45
	/// The Polkadot mainnet Relay-chain.
60
45
	Polkadot,
61
75
	/// The Kusama canary-net Relay-chain.
62
75
	Kusama,
63
72
	/// The Westend testnet Relay-chain.
64
72
	Westend,
65
33
	/// The Rococo testnet Relay-chain.
66
33
	Rococo,
67
84
	/// The Wococo testnet Relay-chain.
68
84
	Wococo,
69
57
	/// An Ethereum network specified by its chain ID.
70
	Ethereum {
71
3
		/// The EIP-155 chain ID.
72
3
		#[codec(compact)]
73
3
		chain_id: u64,
74
57
	},
75
57
	/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
76
57
	BitcoinCore,
77
264
	/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
78
264
	BitcoinCash,
79
36
	/// The Polkadot Bulletin chain.
80
36
	PolkadotBulletin,
81
}
82

            
83
impl From<OldNetworkId> for Option<NetworkId> {
84
756
	fn from(old: OldNetworkId) -> Option<NetworkId> {
85
		use OldNetworkId::*;
86
756
		match old {
87
260
			Any => None,
88
418
			Named(_) => None,
89
28
			Polkadot => Some(NetworkId::Polkadot),
90
50
			Kusama => Some(NetworkId::Kusama),
91
		}
92
756
	}
93
}
94

            
95
impl TryFrom<OldNetworkId> for NetworkId {
96
	type Error = ();
97
	fn try_from(old: OldNetworkId) -> Result<Self, Self::Error> {
98
		use OldNetworkId::*;
99
		match old {
100
			Any | Named(_) => Err(()),
101
			Polkadot => Ok(NetworkId::Polkadot),
102
			Kusama => Ok(NetworkId::Kusama),
103
		}
104
	}
105
}
106

            
107
impl From<NewNetworkId> for Option<NetworkId> {
108
	fn from(new: NewNetworkId) -> Self {
109
		Some(NetworkId::from(new))
110
	}
111
}
112

            
113
impl From<NewNetworkId> for NetworkId {
114
	fn from(new: NewNetworkId) -> Self {
115
		use NewNetworkId::*;
116
		match new {
117
			ByGenesis(hash) => Self::ByGenesis(hash),
118
			ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
119
			Polkadot => Self::Polkadot,
120
			Kusama => Self::Kusama,
121
			Westend => Self::Westend,
122
			Rococo => Self::Rococo,
123
			Wococo => Self::Wococo,
124
			Ethereum { chain_id } => Self::Ethereum { chain_id },
125
			BitcoinCore => Self::BitcoinCore,
126
			BitcoinCash => Self::BitcoinCash,
127
			PolkadotBulletin => Self::PolkadotBulletin,
128
		}
129
	}
130
}
131

            
132
/// An identifier of a pluralistic body.
133
#[derive(
134
	Copy,
135
	Clone,
136
	Eq,
137
	PartialEq,
138
	Ord,
139
	PartialOrd,
140
	Encode,
141
18
	Decode,
142
	Debug,
143
	TypeInfo,
144
	MaxEncodedLen,
145
	Serialize,
146
	Deserialize,
147
)]
148
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
149
#[scale_info(replace_segment("staging_xcm", "xcm"))]
150
pub enum BodyId {
151
363
	/// The only body in its context.
152
363
	Unit,
153
78
	/// A named body.
154
78
	Moniker([u8; 4]),
155
48
	/// An indexed body.
156
48
	Index(#[codec(compact)] u32),
157
42
	/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
158
42
	Executive,
159
126
	/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
160
126
	Technical,
161
27
	/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a
162
27
	/// majority of lock-voters).
163
27
	Legislative,
164
30
	/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a
165
30
	/// "grand oracle", it may be considered as that).
166
30
	Judicial,
167
54
	/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public
168
54
	/// referendum on the `staking_admin` track).
169
54
	Defense,
170
141
	/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a
171
141
	/// public referendum on the `general_admin` track).
172
141
	Administration,
173
18
	/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public
174
18
	/// referendum on the `treasurer` track).
175
18
	Treasury,
176
}
177

            
178
impl TryFrom<OldBodyId> for BodyId {
179
	type Error = ();
180
300
	fn try_from(value: OldBodyId) -> Result<Self, ()> {
181
		use OldBodyId::*;
182
300
		Ok(match value {
183
166
			Unit => Self::Unit,
184
66
			Named(n) =>
185
66
				if n.len() == 4 {
186
2
					let mut r = [0u8; 4];
187
2
					r.copy_from_slice(&n[..]);
188
2
					Self::Moniker(r)
189
				} else {
190
64
					return Err(())
191
				},
192
8
			Index(n) => Self::Index(n),
193
4
			Executive => Self::Executive,
194
32
			Technical => Self::Technical,
195
4
			Legislative => Self::Legislative,
196
2
			Judicial => Self::Judicial,
197
2
			Defense => Self::Defense,
198
14
			Administration => Self::Administration,
199
2
			Treasury => Self::Treasury,
200
		})
201
300
	}
202
}
203

            
204
/// A part of a pluralistic body.
205
#[derive(
206
	Copy,
207
	Clone,
208
	Eq,
209
	PartialEq,
210
	Ord,
211
	PartialOrd,
212
	Encode,
213
63
	Decode,
214
	Debug,
215
	TypeInfo,
216
	MaxEncodedLen,
217
	Serialize,
218
	Deserialize,
219
)]
220
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
221
#[scale_info(replace_segment("staging_xcm", "xcm"))]
222
pub enum BodyPart {
223
438
	/// The body's declaration, under whatever means it decides.
224
438
	Voice,
225
63
	/// A given number of members of the body.
226
	Members {
227
6
		#[codec(compact)]
228
6
		count: u32,
229
63
	},
230
15
	/// A given number of members of the body, out of some larger caucus.
231
	Fraction {
232
		#[codec(compact)]
233
		nom: u32,
234
3
		#[codec(compact)]
235
3
		denom: u32,
236
15
	},
237
51
	/// No less than the given proportion of members of the body.
238
	AtLeastProportion {
239
3
		#[codec(compact)]
240
3
		nom: u32,
241
9
		#[codec(compact)]
242
9
		denom: u32,
243
51
	},
244
291
	/// More than the given proportion of members of the body.
245
	MoreThanProportion {
246
3
		#[codec(compact)]
247
3
		nom: u32,
248
9
		#[codec(compact)]
249
9
		denom: u32,
250
291
	},
251
}
252

            
253
impl BodyPart {
254
	/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
255
	pub fn is_majority(&self) -> bool {
256
		match self {
257
			BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
258
			BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
259
			BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
260
			_ => false,
261
		}
262
	}
263
}
264

            
265
impl TryFrom<OldBodyPart> for BodyPart {
266
	type Error = ();
267
236
	fn try_from(value: OldBodyPart) -> Result<Self, ()> {
268
		use OldBodyPart::*;
269
236
		Ok(match value {
270
170
			Voice => Self::Voice,
271
18
			Members { count } => Self::Members { count },
272
2
			Fraction { nom, denom } => Self::Fraction { nom, denom },
273
20
			AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom },
274
26
			MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom },
275
		})
276
236
	}
277
}
278

            
279
/// A single item in a path to describe the relative location of a consensus system.
280
///
281
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
282
#[derive(
283
	Copy,
284
	Clone,
285
	Eq,
286
	PartialEq,
287
	Ord,
288
	PartialOrd,
289
	Encode,
290
270
	Decode,
291
	Debug,
292
	TypeInfo,
293
	MaxEncodedLen,
294
	Serialize,
295
	Deserialize,
296
)]
297
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
298
#[scale_info(replace_segment("staging_xcm", "xcm"))]
299
pub enum Junction {
300
13305
	/// An indexed parachain belonging to and operated by the context.
301
	///
302
	/// Generally used when the context is a Polkadot Relay-chain.
303
13305
	Parachain(#[codec(compact)] u32),
304
1152
	/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
305
	/// endpoint within the context.
306
	///
307
	/// Generally used when the context is a Substrate-based chain.
308
1152
	AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
309
555
	/// An 8-byte index for an account of a specific network that is respected as a sovereign
310
	/// endpoint within the context.
311
	///
312
	/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
313
	AccountIndex64 {
314
15
		network: Option<NetworkId>,
315
9
		#[codec(compact)]
316
9
		index: u64,
317
555
	},
318
276
	/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
319
	/// endpoint within the context.
320
	///
321
	/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
322
276
	AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
323
1086
	/// An instanced, indexed pallet that forms a constituent part of the context.
324
	///
325
	/// Generally used when the context is a Frame-based chain.
326
	// TODO XCMv4 inner should be `Compact<u32>`.
327
1086
	PalletInstance(u8),
328
249
	/// A non-descript index within the context location.
329
	///
330
	/// Usage will vary widely owing to its generality.
331
	///
332
	/// NOTE: Try to avoid using this and instead use a more specific item.
333
249
	GeneralIndex(#[codec(compact)] u128),
334
177
	/// A nondescript array datum, 32 bytes, acting as a key within the context
335
	/// location.
336
	///
337
	/// Usage will vary widely owing to its generality.
338
	///
339
	/// NOTE: Try to avoid using this and instead use a more specific item.
340
	// Note this is implemented as an array with a length rather than using `BoundedVec` owing to
341
	// the bound for `Copy`.
342
177
	GeneralKey { length: u8, data: [u8; 32] },
343
210
	/// The unambiguous child.
344
210
	///
345
210
	/// Not currently used except as a fallback when deriving context.
346
210
	OnlyChild,
347
177
	/// A pluralistic body existing within consensus.
348
	///
349
	/// Typical to be used to represent a governance origin of a chain, but could in principle be
350
	/// used to represent things such as multisigs also.
351
177
	Plurality { id: BodyId, part: BodyPart },
352
366
	/// A global network capable of externalizing its own consensus. This is not generally
353
	/// meaningful outside of the universal level.
354
366
	GlobalConsensus(NetworkId),
355
}
356

            
357
impl From<NetworkId> for Junction {
358
	fn from(n: NetworkId) -> Self {
359
		Self::GlobalConsensus(n)
360
	}
361
}
362

            
363
impl From<[u8; 32]> for Junction {
364
	fn from(id: [u8; 32]) -> Self {
365
		Self::AccountId32 { network: None, id }
366
	}
367
}
368

            
369
impl From<BoundedVec<u8, ConstU32<32>>> for Junction {
370
	fn from(key: BoundedVec<u8, ConstU32<32>>) -> Self {
371
		key.as_bounded_slice().into()
372
	}
373
}
374

            
375
impl<'a> From<BoundedSlice<'a, u8, ConstU32<32>>> for Junction {
376
	fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self {
377
		let mut data = [0u8; 32];
378
		data[..key.len()].copy_from_slice(&key[..]);
379
		Self::GeneralKey { length: key.len() as u8, data }
380
	}
381
}
382

            
383
impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> {
384
	type Error = ();
385
	fn try_from(key: &'a Junction) -> Result<Self, ()> {
386
		match key {
387
			Junction::GeneralKey { length, data } =>
388
				BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()),
389
			_ => Err(()),
390
		}
391
	}
392
}
393

            
394
impl From<[u8; 20]> for Junction {
395
	fn from(key: [u8; 20]) -> Self {
396
		Self::AccountKey20 { network: None, key }
397
	}
398
}
399

            
400
impl From<u64> for Junction {
401
	fn from(index: u64) -> Self {
402
		Self::AccountIndex64 { network: None, index }
403
	}
404
}
405

            
406
impl From<u128> for Junction {
407
	fn from(id: u128) -> Self {
408
		Self::GeneralIndex(id)
409
	}
410
}
411

            
412
impl TryFrom<OldJunction> for Junction {
413
	type Error = ();
414
5358
	fn try_from(value: OldJunction) -> Result<Self, ()> {
415
		use OldJunction::*;
416
5358
		Ok(match value {
417
3608
			Parachain(id) => Self::Parachain(id),
418
432
			AccountId32 { network, id } => Self::AccountId32 { network: network.into(), id },
419
178
			AccountIndex64 { network, index } =>
420
178
				Self::AccountIndex64 { network: network.into(), index },
421
146
			AccountKey20 { network, key } => Self::AccountKey20 { network: network.into(), key },
422
474
			PalletInstance(index) => Self::PalletInstance(index),
423
58
			GeneralIndex(id) => Self::GeneralIndex(id),
424
78
			GeneralKey(key) => match key.len() {
425
78
				len @ 0..=32 => Self::GeneralKey {
426
66
					length: len as u8,
427
66
					data: {
428
66
						let mut data = [0u8; 32];
429
66
						data[..len].copy_from_slice(&key[..]);
430
66
						data
431
66
					},
432
66
				},
433
12
				_ => return Err(()),
434
			},
435
84
			OnlyChild => Self::OnlyChild,
436
300
			Plurality { id, part } =>
437
300
				Self::Plurality { id: id.try_into()?, part: part.try_into()? },
438
		})
439
5358
	}
440
}
441

            
442
impl TryFrom<NewJunction> for Junction {
443
	type Error = ();
444

            
445
	fn try_from(value: NewJunction) -> Result<Self, Self::Error> {
446
		use NewJunction::*;
447
		Ok(match value {
448
			Parachain(id) => Self::Parachain(id),
449
			AccountId32 { network: maybe_network, id } =>
450
				Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
451
			AccountIndex64 { network: maybe_network, index } =>
452
				Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
453
			AccountKey20 { network: maybe_network, key } =>
454
				Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
455
			PalletInstance(index) => Self::PalletInstance(index),
456
			GeneralIndex(id) => Self::GeneralIndex(id),
457
			GeneralKey { length, data } => Self::GeneralKey { length, data },
458
			OnlyChild => Self::OnlyChild,
459
			Plurality { id, part } => Self::Plurality { id, part },
460
			GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
461
		})
462
	}
463
}
464

            
465
impl Junction {
466
	/// Convert `self` into a `MultiLocation` containing 0 parents.
467
	///
468
	/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
469
	pub const fn into_location(self) -> MultiLocation {
470
		MultiLocation { parents: 0, interior: Junctions::X1(self) }
471
	}
472

            
473
	/// Convert `self` into a `MultiLocation` containing `n` parents.
474
	///
475
	/// Similar to `Self::into_location`, with the added ability to specify the number of parent
476
	/// junctions.
477
	pub const fn into_exterior(self, n: u8) -> MultiLocation {
478
		MultiLocation { parents: n, interior: Junctions::X1(self) }
479
	}
480

            
481
	/// Convert `self` into a `VersionedLocation` containing 0 parents.
482
	///
483
	/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
484
	pub const fn into_versioned(self) -> VersionedLocation {
485
		self.into_location().into_versioned()
486
	}
487

            
488
	/// Remove the `NetworkId` value.
489
	pub fn remove_network_id(&mut self) {
490
		use Junction::*;
491
		match self {
492
			AccountId32 { ref mut network, .. } |
493
			AccountIndex64 { ref mut network, .. } |
494
			AccountKey20 { ref mut network, .. } => *network = None,
495
			_ => {},
496
		}
497
	}
498
}
499

            
500
#[cfg(test)]
501
mod tests {
502
	use super::*;
503
	use alloc::vec;
504

            
505
	#[test]
506
	fn junction_round_trip_works() {
507
		let j = Junction::GeneralKey { length: 32, data: [1u8; 32] };
508
		let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
509
		assert_eq!(j, k);
510

            
511
		let j = OldJunction::GeneralKey(vec![1u8; 32].try_into().unwrap());
512
		let k = OldJunction::try_from(Junction::try_from(j.clone()).unwrap()).unwrap();
513
		assert_eq!(j, k);
514

            
515
		let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap());
516
		let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
517
		assert_eq!(j, k);
518
		let s: BoundedSlice<_, _> = (&k).try_into().unwrap();
519
		assert_eq!(s, &[1u8, 2, 3, 4][..]);
520

            
521
		let j = OldJunction::GeneralKey(vec![1u8, 2, 3, 4].try_into().unwrap());
522
		let k = OldJunction::try_from(Junction::try_from(j.clone()).unwrap()).unwrap();
523
		assert_eq!(j, k);
524
	}
525
}