tp_version/
lib.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Version module for the Tetcore runtime; Provides a function that returns the runtime version.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22#[cfg(feature = "std")]
23use serde::{Serialize, Deserialize};
24#[cfg(feature = "std")]
25use std::fmt;
26#[cfg(feature = "std")]
27use std::collections::HashSet;
28
29use codec::{Encode, Decode};
30use tp_runtime::RuntimeString;
31pub use tp_runtime::create_runtime_str;
32#[doc(hidden)]
33pub use tetcore_std;
34
35#[cfg(feature = "std")]
36use tp_runtime::{traits::Block as BlockT, generic::BlockId};
37
38/// The identity of a particular API interface that the runtime might provide.
39pub type ApiId = [u8; 8];
40
41/// A vector of pairs of `ApiId` and a `u32` for version.
42pub type ApisVec = tetcore_std::borrow::Cow<'static, [(ApiId, u32)]>;
43
44/// Create a vector of Api declarations.
45#[macro_export]
46macro_rules! create_apis_vec {
47	( $y:expr ) => { $crate::tetcore_std::borrow::Cow::Borrowed(& $y) }
48}
49
50/// Runtime version.
51/// This should not be thought of as classic Semver (major/minor/tiny).
52/// This triplet have different semantics and mis-interpretation could cause problems.
53/// In particular: bug fixes should result in an increment of `spec_version` and possibly `authoring_version`,
54/// absolutely not `impl_version` since they change the semantics of the runtime.
55#[derive(Clone, PartialEq, Eq, Encode, Decode, Default, tp_runtime::RuntimeDebug)]
56#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
57#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
58pub struct RuntimeVersion {
59	/// Identifies the different Tetcore runtimes. There'll be at least tetcoin and node.
60	/// A different on-chain spec_name to that of the native runtime would normally result
61	/// in node not attempting to sync or author blocks.
62	pub spec_name: RuntimeString,
63
64	/// Name of the implementation of the spec. This is of little consequence for the node
65	/// and serves only to differentiate code of different implementation teams. For this
66	/// codebase, it will be tetsy-tetcoin. If there were a non-Rust implementation of the
67	/// Tetcoin runtime (e.g. C++), then it would identify itself with an accordingly different
68	/// `impl_name`.
69	pub impl_name: RuntimeString,
70
71	/// `authoring_version` is the version of the authorship interface. An authoring node
72	/// will not attempt to author blocks unless this is equal to its native runtime.
73	pub authoring_version: u32,
74
75	/// Version of the runtime specification. A full-node will not attempt to use its native
76	/// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
77	/// `spec_version` and `authoring_version` are the same between Wasm and native.
78	pub spec_version: u32,
79
80	/// Version of the implementation of the specification. Nodes are free to ignore this; it
81	/// serves only as an indication that the code is different; as long as the other two versions
82	/// are the same then while the actual code may be different, it is nonetheless required to
83	/// do the same thing.
84	/// Non-consensus-breaking optimizations are about the only changes that could be made which
85	/// would result in only the `impl_version` changing.
86	pub impl_version: u32,
87
88	/// List of supported API "features" along with their versions.
89	#[cfg_attr(
90		feature = "std",
91		serde(
92			serialize_with = "apis_serialize::serialize",
93			deserialize_with = "apis_serialize::deserialize",
94		)
95	)]
96	pub apis: ApisVec,
97
98	/// All existing dispatches are fully compatible when this number doesn't change. If this
99	/// number changes, then `spec_version` must change, also.
100	///
101	/// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
102	/// either through an alteration in its user-level semantics, a parameter added/removed/changed,
103	/// a dispatchable being removed, a module being removed, or a dispatchable/module changing its
104	/// index.
105	///
106	/// It need *not* change when a new module is added or when a dispatchable is added.
107	pub transaction_version: u32,
108}
109
110#[cfg(feature = "std")]
111impl fmt::Display for RuntimeVersion {
112	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113		write!(f, "{}-{} ({}-{}.tx{}.au{})",
114			self.spec_name,
115			self.spec_version,
116			self.impl_name,
117			self.impl_version,
118			self.transaction_version,
119			self.authoring_version,
120		)
121	}
122}
123
124#[cfg(feature = "std")]
125impl RuntimeVersion {
126	/// Check if this version matches other version for calling into runtime.
127	pub fn can_call_with(&self, other: &RuntimeVersion) -> bool {
128		self.spec_version == other.spec_version &&
129		self.spec_name == other.spec_name &&
130		self.authoring_version == other.authoring_version
131	}
132
133	/// Check if the given api with `api_id` is implemented and the version passes the given
134	/// `predicate`.
135	pub fn has_api_with<P: Fn(u32) -> bool>(
136		&self,
137		id: &ApiId,
138		predicate: P,
139	) -> bool {
140		self.apis.iter().any(|(s, v)| s == id && predicate(*v))
141	}
142}
143
144#[cfg(feature = "std")]
145#[derive(Debug)]
146pub struct NativeVersion {
147	/// Basic runtime version info.
148	pub runtime_version: RuntimeVersion,
149	/// Authoring runtimes that this native runtime supports.
150	pub can_author_with: HashSet<u32>,
151}
152
153#[cfg(feature = "std")]
154impl NativeVersion {
155	/// Check if this version matches other version for authoring blocks.
156	///
157	/// # Return
158	///
159	/// - Returns `Ok(())` when authoring is supported.
160	/// - Returns `Err(_)` with a detailed error when authoring is not supported.
161	pub fn can_author_with(&self, other: &RuntimeVersion) -> Result<(), String> {
162		if self.runtime_version.spec_name != other.spec_name {
163			Err(format!(
164				"`spec_name` does not match `{}` vs `{}`",
165				self.runtime_version.spec_name,
166				other.spec_name,
167			))
168		} else if self.runtime_version.authoring_version != other.authoring_version
169			&& !self.can_author_with.contains(&other.authoring_version)
170		{
171			Err(format!(
172				"`authoring_version` does not match `{version}` vs `{other_version}` and \
173				`can_author_with` not contains `{other_version}`",
174				version = self.runtime_version.authoring_version,
175				other_version = other.authoring_version,
176			))
177		} else {
178			Ok(())
179		}
180	}
181}
182
183/// Something that can provide the runtime version at a given block and the native runtime version.
184#[cfg(feature = "std")]
185pub trait GetRuntimeVersion<Block: BlockT> {
186	/// Returns the version of the native runtime.
187	fn native_version(&self) -> &NativeVersion;
188
189	/// Returns the version of runtime at the given block.
190	fn runtime_version(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, String>;
191}
192
193#[cfg(feature = "std")]
194impl<T: GetRuntimeVersion<Block>, Block: BlockT> GetRuntimeVersion<Block> for std::sync::Arc<T> {
195	fn native_version(&self) -> &NativeVersion {
196		(&**self).native_version()
197	}
198
199	fn runtime_version(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, String> {
200		(&**self).runtime_version(at)
201	}
202}
203
204#[cfg(feature = "std")]
205mod apis_serialize {
206	use super::*;
207	use impl_serde::serialize as bytes;
208	use serde::{Serializer, de, ser::SerializeTuple};
209
210	#[derive(Serialize)]
211	struct ApiId<'a>(
212		#[serde(serialize_with="serialize_bytesref")] &'a super::ApiId,
213		&'a u32,
214	);
215
216	pub fn serialize<S>(apis: &ApisVec, ser: S) -> Result<S::Ok, S::Error> where
217		S: Serializer,
218	{
219		let len = apis.len();
220		let mut seq = ser.serialize_tuple(len)?;
221		for (api, ver) in &**apis {
222			seq.serialize_element(&ApiId(api, ver))?;
223		}
224		seq.end()
225	}
226
227	pub fn serialize_bytesref<S>(&apis: &&super::ApiId, ser: S) -> Result<S::Ok, S::Error> where
228		S: Serializer,
229	{
230		bytes::serialize(apis, ser)
231	}
232
233	#[derive(Deserialize)]
234	struct ApiIdOwned(
235		#[serde(deserialize_with="deserialize_bytes")]
236		super::ApiId,
237		u32,
238	);
239
240	pub fn deserialize<'de, D>(deserializer: D) -> Result<ApisVec, D::Error> where
241		D: de::Deserializer<'de>,
242	{
243		struct Visitor;
244		impl<'de> de::Visitor<'de> for Visitor {
245			type Value = ApisVec;
246
247			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
248				formatter.write_str("a sequence of api id and version tuples")
249			}
250
251			fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> where
252				V: de::SeqAccess<'de>,
253			{
254				let mut apis = Vec::new();
255				while let Some(value) = visitor.next_element::<ApiIdOwned>()? {
256					apis.push((value.0, value.1));
257				}
258				Ok(apis.into())
259			}
260		}
261		deserializer.deserialize_seq(Visitor)
262	}
263
264	pub fn deserialize_bytes<'de, D>(d: D) -> Result<super::ApiId, D::Error> where
265		D: de::Deserializer<'de>
266	{
267		let mut arr = [0; 8];
268		bytes::deserialize_check_len(d, bytes::ExpectedLen::Exact(&mut arr[..]))?;
269		Ok(arr)
270	}
271}