1use crate::executor::host;
24
25use alloc::vec::Vec;
26use core::{fmt, ops, str};
27
28pub use super::TrieEntryVersion;
29
30pub fn find_embedded_runtime_version(
32 binary_wasm_module: &[u8],
33) -> Result<Option<CoreVersion>, FindEmbeddedRuntimeVersionError> {
34 let (runtime_version_content, runtime_apis_content) =
35 match find_encoded_embedded_runtime_version_apis(binary_wasm_module) {
36 Ok(EmbeddedRuntimeVersionApis {
37 runtime_version_content: Some(v),
38 runtime_apis_content: Some(a),
39 }) => (v, a),
40 Ok(EmbeddedRuntimeVersionApis {
41 runtime_version_content: None,
42 runtime_apis_content: None,
43 }) => return Ok(None),
44 Ok(_) => return Err(FindEmbeddedRuntimeVersionError::CustomSectionsPresenceMismatch),
45 Err(err) => return Err(FindEmbeddedRuntimeVersionError::FindSections(err)),
46 };
47
48 let mut decoded_runtime_version = match decode(runtime_version_content) {
49 Ok(d) => d,
50 Err(()) => return Err(FindEmbeddedRuntimeVersionError::RuntimeVersionDecode),
51 };
52
53 decoded_runtime_version.apis =
54 match CoreVersionApisRefIter::from_slice_no_length(runtime_apis_content) {
55 Ok(d) => d,
56 Err(err) => return Err(FindEmbeddedRuntimeVersionError::RuntimeApisDecode(err)),
57 };
58
59 Ok(Some(CoreVersion(
60 decoded_runtime_version.scale_encoding_vec(),
61 )))
62}
63
64#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
66pub enum FindEmbeddedRuntimeVersionError {
67 #[display("{_0}")]
69 FindSections(FindEncodedEmbeddedRuntimeVersionApisError),
70 CustomSectionsPresenceMismatch,
72 RuntimeVersionDecode,
74 #[display("{_0}")]
76 RuntimeApisDecode(CoreVersionApisFromSliceErr),
77}
78
79#[derive(Debug, Copy, Clone)]
81pub struct EmbeddedRuntimeVersionApis<'a> {
82 pub runtime_version_content: Option<&'a [u8]>,
84 pub runtime_apis_content: Option<&'a [u8]>,
86}
87
88pub fn find_encoded_embedded_runtime_version_apis(
92 binary_wasm_module: &'_ [u8],
93) -> Result<EmbeddedRuntimeVersionApis<'_>, FindEncodedEmbeddedRuntimeVersionApisError> {
94 let mut parser =
95 nom::combinator::all_consuming(nom::combinator::complete(nom::sequence::preceded(
96 (
97 nom::bytes::streaming::tag(&b"\0asm"[..]),
98 nom::bytes::streaming::tag(&[0x1, 0x0, 0x0, 0x0][..]),
99 ),
100 nom::multi::fold_many0(
101 nom::combinator::complete(wasm_section),
102 || (None, None),
103 move |prev_found, in_section| {
104 match (prev_found, in_section) {
105 (prev_found, None) => prev_found,
107
108 (
112 prev_found @ (Some(_), _),
113 Some(WasmSection {
114 name: b"runtime_version",
115 ..
116 }),
117 ) => prev_found,
118 (
119 prev_found @ (_, Some(_)),
120 Some(WasmSection {
121 name: b"runtime_apis",
122 ..
123 }),
124 ) => prev_found,
125
126 (
129 (None, prev_rt_apis),
130 Some(WasmSection {
131 name: b"runtime_version",
132 content,
133 }),
134 ) => (Some(content), prev_rt_apis),
135 (
136 (prev_rt_version, None),
137 Some(WasmSection {
138 name: b"runtime_apis",
139 content,
140 }),
141 ) => (prev_rt_version, Some(content)),
142
143 (prev_found, Some(_)) => prev_found,
145 }
146 },
147 ),
148 )));
149
150 let (runtime_version_content, runtime_apis_content) =
151 match nom::Parser::parse(&mut parser, binary_wasm_module) {
152 Ok((_, content)) => content,
153 Err(_) => return Err(FindEncodedEmbeddedRuntimeVersionApisError::FailedToParse),
154 };
155
156 Ok(EmbeddedRuntimeVersionApis {
157 runtime_version_content,
158 runtime_apis_content,
159 })
160}
161
162#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
164pub enum FindEncodedEmbeddedRuntimeVersionApisError {
165 FailedToParse,
167}
168
169#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
171pub enum CoreVersionError {
172 Decode,
174 #[display("Error while starting the execution of the `Core_version` function: {_0}")]
176 Start(host::StartErr),
177 #[display("Error during the execution of the `Core_version` function: {_0}")]
179 Run(host::Error),
180 ForbiddenHostFunction,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct CoreVersion(Vec<u8>);
187
188impl CoreVersion {
189 pub fn from_slice(input: Vec<u8>) -> Result<Self, Vec<u8>> {
190 if decode(&input).is_err() {
191 return Err(input);
192 }
193
194 Ok(CoreVersion(input))
195 }
196
197 pub fn decode(&'_ self) -> CoreVersionRef<'_> {
198 decode(&self.0).unwrap()
199 }
200}
201
202impl AsRef<[u8]> for CoreVersion {
203 fn as_ref(&self) -> &[u8] {
204 &self.0
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct CoreVersionRef<'a> {
212 pub spec_name: &'a str,
213 pub impl_name: &'a str,
214 pub authoring_version: u32,
215 pub spec_version: u32,
216 pub impl_version: u32,
217
218 pub apis: CoreVersionApisRefIter<'a>,
225
226 pub transaction_version: Option<u32>,
233
234 pub state_version: Option<TrieEntryVersion>,
243}
244
245impl CoreVersionRef<'_> {
246 pub fn scale_encoding_vec(&self) -> Vec<u8> {
248 let num_apis = self.apis.clone().count();
251
252 let mut out = Vec::<u8>::with_capacity(
256 2 + self.spec_name.len() + 2 + self.impl_name.len() + 4 + 4 + 4 + num_apis * 12 + 4 + 1,
257 );
258
259 out.extend(crate::util::encode_scale_compact_usize(self.spec_name.len()).as_ref());
260 out.extend(self.spec_name.as_bytes());
261
262 out.extend(crate::util::encode_scale_compact_usize(self.impl_name.len()).as_ref());
263 out.extend(self.impl_name.as_bytes());
264
265 out.extend(self.authoring_version.to_le_bytes());
266 out.extend(self.spec_version.to_le_bytes());
267 out.extend(self.impl_version.to_le_bytes());
268
269 out.extend(crate::util::encode_scale_compact_usize(num_apis).as_ref());
270 for api in self.apis.clone() {
271 out.extend(api.name_hash);
272 out.extend(api.version.to_le_bytes());
273 }
274
275 if let Some(transaction_version) = self.transaction_version {
276 out.extend(transaction_version.to_le_bytes());
277 }
278
279 if let Some(state_version) = self.state_version {
281 out.extend(u8::from(state_version).to_le_bytes());
282 }
283
284 out
285 }
286}
287
288#[derive(Clone)]
290pub struct CoreVersionApisRefIter<'a> {
291 inner: &'a [u8],
292}
293
294impl<'a> CoreVersionApisRefIter<'a> {
295 pub fn from_slice_no_length(input: &'a [u8]) -> Result<Self, CoreVersionApisFromSliceErr> {
299 let result: Result<_, nom::Err<nom::error::Error<&[u8]>>> = nom::Parser::parse(
300 &mut nom::combinator::all_consuming(nom::combinator::complete(nom::combinator::map(
301 nom::combinator::recognize(nom::multi::fold_many0(
302 nom::combinator::complete(core_version_api),
303 || {},
304 |(), _| (),
305 )),
306 |inner| CoreVersionApisRefIter { inner },
307 ))),
308 input,
309 );
310
311 match result {
312 Ok((_, me)) => Ok(me),
313 Err(_) => Err(CoreVersionApisFromSliceErr()),
314 }
315 }
316
317 pub fn find_version(&self, api: &str) -> Option<u32> {
326 self.find_versions([api])[0]
327 }
328
329 pub fn find_versions<const N: usize>(&self, apis: [&str; N]) -> [Option<u32>; N] {
332 let hashed = core::array::from_fn::<_, N, _>(|n| hash_api_name(apis[n]));
333 let mut out = [None; N];
334
335 for api in self.clone() {
336 for (n, expected) in hashed.iter().enumerate() {
337 if *expected == api.name_hash {
338 match out[n] {
339 Some(ref mut v) if *v < api.version => *v = api.version,
340 Some(_) => {}
341 ref mut v @ None => *v = Some(api.version),
342 }
343 }
344 }
345 }
346
347 out
348 }
349
350 pub fn contains(&self, api_name: &str, version_number: impl ops::RangeBounds<u32>) -> bool {
358 self.contains_hashed(&hash_api_name(api_name), version_number)
359 }
360
361 pub fn contains_hashed(
364 &self,
365 api_name_hash: &[u8; 8],
366 version_number: impl ops::RangeBounds<u32>,
367 ) -> bool {
368 self.clone()
369 .any(|api| api.name_hash == *api_name_hash && version_number.contains(&api.version))
370 }
371}
372
373impl Iterator for CoreVersionApisRefIter<'_> {
374 type Item = CoreVersionApi;
375
376 fn next(&mut self) -> Option<Self::Item> {
377 if self.inner.is_empty() {
378 return None;
379 }
380
381 match core_version_api::<nom::error::Error<&[u8]>>(self.inner) {
382 Ok((rest, item)) => {
383 self.inner = rest;
384 Some(item)
385 }
386
387 Err(_) => unreachable!(),
390 }
391 }
392}
393
394impl PartialEq for CoreVersionApisRefIter<'_> {
395 fn eq(&self, other: &Self) -> bool {
396 let mut a = self.clone();
397 let mut b = other.clone();
398 loop {
399 match (a.next(), b.next()) {
400 (Some(a), Some(b)) if a == b => {}
401 (None, None) => return true,
402 _ => return false,
403 }
404 }
405 }
406}
407
408impl Eq for CoreVersionApisRefIter<'_> {}
409
410impl fmt::Debug for CoreVersionApisRefIter<'_> {
411 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412 f.debug_list().entries(self.clone()).finish()
413 }
414}
415
416#[derive(Debug, Clone, derive_more::Display, derive_more::Error)]
418#[display("Error decoding core version APIs")]
419pub struct CoreVersionApisFromSliceErr();
420
421pub fn hash_api_name(api_name: &str) -> [u8; 8] {
423 let result = blake2_rfc::blake2b::blake2b(8, &[], api_name.as_bytes());
424 result.as_bytes().try_into().unwrap()
425}
426
427#[derive(Debug, Clone, PartialEq, Eq, Hash)]
429pub struct CoreVersionApi {
430 pub name_hash: [u8; 8],
436
437 pub version: u32,
439}
440
441fn decode(scale_encoded: &'_ [u8]) -> Result<CoreVersionRef<'_>, ()> {
442 let result: nom::IResult<_, _> = nom::Parser::parse(
444 &mut nom::combinator::all_consuming(nom::combinator::complete(nom::combinator::map(
445 (
446 crate::util::nom_string_decode,
447 crate::util::nom_string_decode,
448 nom::number::streaming::le_u32,
449 nom::number::streaming::le_u32,
450 nom::number::streaming::le_u32,
451 core_version_apis,
452 nom::branch::alt((
453 nom::combinator::complete(nom::combinator::map(
454 nom::number::streaming::le_u32,
455 Some,
456 )),
457 nom::combinator::map(nom::combinator::eof, |_| None),
458 )),
459 nom::branch::alt((
460 nom::combinator::complete(nom::combinator::map(
461 nom::bytes::streaming::tag(&[0][..]),
462 |_| Some(TrieEntryVersion::V0),
463 )),
464 nom::combinator::complete(nom::combinator::map(
465 nom::bytes::streaming::tag(&[1][..]),
466 |_| Some(TrieEntryVersion::V1),
467 )),
468 nom::combinator::map(nom::combinator::eof, |_| None),
469 )),
470 ),
471 |(
472 spec_name,
473 impl_name,
474 authoring_version,
475 spec_version,
476 impl_version,
477 apis,
478 transaction_version,
479 state_version,
480 )| CoreVersionRef {
481 spec_name,
482 impl_name,
483 authoring_version,
484 spec_version,
485 impl_version,
486 apis,
487 transaction_version,
488 state_version,
489 },
490 ))),
491 scale_encoded,
492 );
493
494 match result {
495 Ok((_, out)) => Ok(out),
496 Err(nom::Err::Error(_) | nom::Err::Failure(_)) => Err(()),
497 Err(_) => unreachable!(),
498 }
499}
500
501fn core_version_apis<'a, E: nom::error::ParseError<&'a [u8]>>(
502 bytes: &'a [u8],
503) -> nom::IResult<&'a [u8], CoreVersionApisRefIter<'a>, E> {
504 nom::Parser::parse(
505 &mut nom::combinator::map(
506 nom::combinator::flat_map(crate::util::nom_scale_compact_usize, |num_elems| {
507 nom::combinator::recognize(nom::multi::fold_many_m_n(
508 num_elems,
509 num_elems,
510 core_version_api,
511 || {},
512 |(), _| (),
513 ))
514 }),
515 |inner| CoreVersionApisRefIter { inner },
516 ),
517 bytes,
518 )
519}
520
521fn core_version_api<'a, E: nom::error::ParseError<&'a [u8]>>(
522 bytes: &'a [u8],
523) -> nom::IResult<&'a [u8], CoreVersionApi, E> {
524 nom::Parser::parse(
525 &mut nom::combinator::map(
526 (
527 nom::bytes::streaming::take(8u32),
528 nom::number::streaming::le_u32,
529 ),
530 move |(name, version)| CoreVersionApi {
531 name_hash: <[u8; 8]>::try_from(name).unwrap(),
532 version,
533 },
534 ),
535 bytes,
536 )
537}
538
539struct WasmSection<'a> {
540 name: &'a [u8],
541 content: &'a [u8],
542}
543
544fn wasm_section(bytes: &'_ [u8]) -> nom::IResult<&'_ [u8], Option<WasmSection<'_>>> {
546 nom::Parser::parse(
547 &mut nom::branch::alt((
548 nom::combinator::map(
549 nom::combinator::map_parser(
550 nom::sequence::preceded(
551 nom::bytes::streaming::tag(&[0][..]),
552 nom::multi::length_data(nom::combinator::map_opt(
553 crate::util::leb128::nom_leb128_u64,
554 |n| u32::try_from(n).ok(),
555 )),
556 ),
557 (
558 nom::multi::length_data(nom::combinator::map_opt(
559 crate::util::leb128::nom_leb128_u64,
560 |n| u32::try_from(n).ok(),
561 )),
562 nom::combinator::rest,
563 ),
564 ),
565 |(name, content)| Some(WasmSection { name, content }),
566 ),
567 nom::combinator::map(
568 (
569 nom::number::streaming::u8,
570 nom::multi::length_data(nom::combinator::map_opt(
571 crate::util::leb128::nom_leb128_u64,
572 |n| u32::try_from(n).ok(),
573 )),
574 ),
575 |_| None,
576 ),
577 )),
578 bytes,
579 )
580}