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 `Location`, primarily the `Junction` datatype.
18

            
19
use super::Location;
20
pub use crate::v3::{BodyId, BodyPart};
21
use crate::{
22
	v3::{Junction as OldJunction, NetworkId as OldNetworkId},
23
	VersionedLocation,
24
};
25
use bounded_collections::{BoundedSlice, BoundedVec, ConstU32};
26
use codec::{self, Decode, Encode, MaxEncodedLen};
27
use scale_info::TypeInfo;
28
use serde::{Deserialize, Serialize};
29

            
30
/// A single item in a path to describe the relative location of a consensus system.
31
///
32
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
33
#[derive(
34
	Copy,
35
	Clone,
36
	Eq,
37
	PartialEq,
38
	Ord,
39
	PartialOrd,
40
	Encode,
41
225
	Decode,
42
	Debug,
43
	TypeInfo,
44
	MaxEncodedLen,
45
	Serialize,
46
	Deserialize,
47
)]
48
pub enum Junction {
49
24900
	/// An indexed parachain belonging to and operated by the context.
50
	///
51
	/// Generally used when the context is a Polkadot Relay-chain.
52
24900
	Parachain(#[codec(compact)] u32),
53
3930
	/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
54
	/// endpoint within the context.
55
	///
56
	/// Generally used when the context is a Substrate-based chain.
57
3930
	AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
58
246
	/// An 8-byte index for an account of a specific network that is respected as a sovereign
59
	/// endpoint within the context.
60
	///
61
	/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
62
	AccountIndex64 {
63
12
		network: Option<NetworkId>,
64
9
		#[codec(compact)]
65
9
		index: u64,
66
246
	},
67
183
	/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
68
	/// endpoint within the context.
69
	///
70
	/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
71
183
	AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
72
2871
	/// An instanced, indexed pallet that forms a constituent part of the context.
73
	///
74
	/// Generally used when the context is a Frame-based chain.
75
	// TODO XCMv4 inner should be `Compact<u32>`.
76
2871
	PalletInstance(u8),
77
351
	/// A non-descript index within the context location.
78
	///
79
	/// Usage will vary widely owing to its generality.
80
	///
81
	/// NOTE: Try to avoid using this and instead use a more specific item.
82
351
	GeneralIndex(#[codec(compact)] u128),
83
1056
	/// A nondescript array datum, 32 bytes, acting as a key within the context
84
	/// location.
85
	///
86
	/// Usage will vary widely owing to its generality.
87
	///
88
	/// NOTE: Try to avoid using this and instead use a more specific item.
89
	// Note this is implemented as an array with a length rather than using `BoundedVec` owing to
90
	// the bound for `Copy`.
91
1056
	GeneralKey { length: u8, data: [u8; 32] },
92
378
	/// The unambiguous child.
93
378
	///
94
378
	/// Not currently used except as a fallback when deriving context.
95
378
	OnlyChild,
96
768
	/// A pluralistic body existing within consensus.
97
	///
98
	/// Typical to be used to represent a governance origin of a chain, but could in principle be
99
	/// used to represent things such as multisigs also.
100
768
	Plurality { id: BodyId, part: BodyPart },
101
642
	/// A global network capable of externalizing its own consensus. This is not generally
102
	/// meaningful outside of the universal level.
103
642
	GlobalConsensus(NetworkId),
104
}
105

            
106
/// A global identifier of a data structure existing within consensus.
107
///
108
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
109
/// Polkadot ecosystem are given preference over explicit naming in this enumeration.
110
#[derive(
111
	Copy,
112
	Clone,
113
	Eq,
114
	PartialEq,
115
	Ord,
116
	PartialOrd,
117
	Encode,
118
39
	Decode,
119
	Debug,
120
	TypeInfo,
121
	MaxEncodedLen,
122
	Serialize,
123
	Deserialize,
124
)]
125
pub enum NetworkId {
126
1734
	/// Network specified by the first 32 bytes of its genesis block.
127
1734
	ByGenesis([u8; 32]),
128
1677
	/// Network defined by the first 32-bytes of the hash and number of some block it contains.
129
1677
	ByFork { block_number: u64, block_hash: [u8; 32] },
130
24
	/// The Polkadot mainnet Relay-chain.
131
24
	Polkadot,
132
39
	/// The Kusama canary-net Relay-chain.
133
39
	Kusama,
134
48
	/// The Westend testnet Relay-chain.
135
48
	Westend,
136
1161
	/// The Rococo testnet Relay-chain.
137
1161
	Rococo,
138
15
	/// The Wococo testnet Relay-chain.
139
15
	Wococo,
140
111
	/// An Ethereum network specified by its chain ID.
141
	Ethereum {
142
6
		/// The EIP-155 chain ID.
143
6
		#[codec(compact)]
144
6
		chain_id: u64,
145
111
	},
146
102
	/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
147
102
	BitcoinCore,
148
441
	/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
149
441
	BitcoinCash,
150
39
	/// The Polkadot Bulletin chain.
151
39
	PolkadotBulletin,
152
}
153

            
154
impl From<OldNetworkId> for Option<NetworkId> {
155
	fn from(old: OldNetworkId) -> Self {
156
		Some(NetworkId::from(old))
157
	}
158
}
159

            
160
impl From<OldNetworkId> for NetworkId {
161
2290
	fn from(old: OldNetworkId) -> Self {
162
		use OldNetworkId::*;
163
2290
		match old {
164
1095
			ByGenesis(hash) => Self::ByGenesis(hash),
165
395
			ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
166
90
			Polkadot => Self::Polkadot,
167
220
			Kusama => Self::Kusama,
168
45
			Westend => Self::Westend,
169
20
			Rococo => Self::Rococo,
170
95
			Wococo => Self::Wococo,
171
40
			Ethereum { chain_id } => Self::Ethereum { chain_id },
172
25
			BitcoinCore => Self::BitcoinCore,
173
220
			BitcoinCash => Self::BitcoinCash,
174
45
			PolkadotBulletin => Self::PolkadotBulletin,
175
		}
176
2290
	}
177
}
178

            
179
impl From<NetworkId> for Junction {
180
5316
	fn from(n: NetworkId) -> Self {
181
5316
		Self::GlobalConsensus(n)
182
5316
	}
183
}
184

            
185
impl From<[u8; 32]> for Junction {
186
	fn from(id: [u8; 32]) -> Self {
187
		Self::AccountId32 { network: None, id }
188
	}
189
}
190

            
191
impl From<BoundedVec<u8, ConstU32<32>>> for Junction {
192
	fn from(key: BoundedVec<u8, ConstU32<32>>) -> Self {
193
		key.as_bounded_slice().into()
194
	}
195
}
196

            
197
impl<'a> From<BoundedSlice<'a, u8, ConstU32<32>>> for Junction {
198
	fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self {
199
		let mut data = [0u8; 32];
200
		data[..key.len()].copy_from_slice(&key[..]);
201
		Self::GeneralKey { length: key.len() as u8, data }
202
	}
203
}
204

            
205
impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> {
206
	type Error = ();
207
	fn try_from(key: &'a Junction) -> Result<Self, ()> {
208
		match key {
209
			Junction::GeneralKey { length, data } =>
210
				BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()),
211
			_ => Err(()),
212
		}
213
	}
214
}
215

            
216
impl From<[u8; 20]> for Junction {
217
	fn from(key: [u8; 20]) -> Self {
218
		Self::AccountKey20 { network: None, key }
219
	}
220
}
221

            
222
impl From<u64> for Junction {
223
	fn from(index: u64) -> Self {
224
		Self::AccountIndex64 { network: None, index }
225
	}
226
}
227

            
228
impl From<u128> for Junction {
229
	fn from(id: u128) -> Self {
230
		Self::GeneralIndex(id)
231
	}
232
}
233

            
234
impl TryFrom<OldJunction> for Junction {
235
	type Error = ();
236
7572
	fn try_from(value: OldJunction) -> Result<Self, ()> {
237
		use OldJunction::*;
238
7572
		Ok(match value {
239
5394
			Parachain(id) => Self::Parachain(id),
240
572
			AccountId32 { network: maybe_network, id } =>
241
572
				Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
242
190
			AccountIndex64 { network: maybe_network, index } =>
243
190
				Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
244
186
			AccountKey20 { network: maybe_network, key } =>
245
186
				Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
246
550
			PalletInstance(index) => Self::PalletInstance(index),
247
104
			GeneralIndex(id) => Self::GeneralIndex(id),
248
112
			GeneralKey { length, data } => Self::GeneralKey { length, data },
249
100
			OnlyChild => Self::OnlyChild,
250
240
			Plurality { id, part } => Self::Plurality { id, part },
251
124
			GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
252
		})
253
7572
	}
254
}
255

            
256
impl Junction {
257
	/// Convert `self` into a `Location` containing 0 parents.
258
	///
259
	/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
260
1512
	pub fn into_location(self) -> Location {
261
1512
		Location::new(0, [self])
262
1512
	}
263

            
264
	/// Convert `self` into a `Location` containing `n` parents.
265
	///
266
	/// Similar to `Self::into_location`, with the added ability to specify the number of parent
267
	/// junctions.
268
	pub fn into_exterior(self, n: u8) -> Location {
269
		Location::new(n, [self])
270
	}
271

            
272
	/// Convert `self` into a `VersionedLocation` containing 0 parents.
273
	///
274
	/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
275
	pub fn into_versioned(self) -> VersionedLocation {
276
		self.into_location().into_versioned()
277
	}
278

            
279
	/// Remove the `NetworkId` value.
280
	pub fn remove_network_id(&mut self) {
281
		use Junction::*;
282
		match self {
283
			AccountId32 { ref mut network, .. } |
284
			AccountIndex64 { ref mut network, .. } |
285
			AccountKey20 { ref mut network, .. } => *network = None,
286
			_ => {},
287
		}
288
	}
289
}
290

            
291
#[cfg(test)]
292
mod tests {
293
	use super::*;
294
	use alloc::vec;
295

            
296
	#[test]
297
	fn junction_round_trip_works() {
298
		let j = Junction::GeneralKey { length: 32, data: [1u8; 32] };
299
		let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
300
		assert_eq!(j, k);
301

            
302
		let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
303
		let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
304
		assert_eq!(j, k);
305

            
306
		let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap());
307
		let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
308
		assert_eq!(j, k);
309
		let s: BoundedSlice<_, _> = (&k).try_into().unwrap();
310
		assert_eq!(s, &[1u8, 2, 3, 4][..]);
311

            
312
		let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
313
		let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
314
		assert_eq!(j, k);
315
	}
316
}