1#![cfg_attr(not(feature = "std"), no_std)]
18#![deny(missing_docs)]
19
20extern crate alloc;
21
22mod from_into;
23mod utils;
24
25use alloc::borrow::Cow;
26use alloc::string::String;
27use alloc::sync::Arc;
28use alloc::vec::Vec;
29use frame_decode::extrinsics::{
30 ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError,
31 ExtrinsicSignatureInfo,
32};
33use hashbrown::HashMap;
34use scale_info::{form::PortableForm, PortableRegistry, Variant};
35use utils::variant_index::VariantIndex;
36use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes};
37
38type ArcStr = Arc<str>;
39
40use crate::utils::validation::{get_custom_value_hash, HASH_LEN};
41pub use from_into::TryFromError;
42pub use utils::validation::MetadataHasher;
43
44#[derive(Debug, Clone)]
48pub struct Metadata {
49 types: PortableRegistry,
51 pallets: OrderedMap<ArcStr, PalletMetadataInner>,
53 pallets_by_index: HashMap<u8, usize>,
55 extrinsic: ExtrinsicMetadata,
57 runtime_ty: u32,
59 outer_enums: OuterEnumsMetadata,
61 dispatch_error_ty: Option<u32>,
63 apis: OrderedMap<ArcStr, RuntimeApiMetadataInner>,
65 custom: frame_metadata::v15::CustomMetadata<PortableForm>,
67}
68
69impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata {
72 type TypeId = u32;
73
74 fn get_call_info(
75 &self,
76 pallet_index: u8,
77 call_index: u8,
78 ) -> Result<ExtrinsicCallInfo<'_, Self::TypeId>, ExtrinsicInfoError<'_>> {
79 let pallet = self.pallet_by_index(pallet_index).ok_or({
80 ExtrinsicInfoError::PalletNotFound {
81 index: pallet_index,
82 }
83 })?;
84
85 let call = pallet.call_variant_by_index(call_index).ok_or_else(|| {
86 ExtrinsicInfoError::CallNotFound {
87 index: call_index,
88 pallet_index,
89 pallet_name: Cow::Borrowed(pallet.name()),
90 }
91 })?;
92
93 Ok(ExtrinsicCallInfo {
94 pallet_name: Cow::Borrowed(pallet.name()),
95 call_name: Cow::Borrowed(&call.name),
96 args: call
97 .fields
98 .iter()
99 .map(|f| ExtrinsicInfoArg {
100 name: Cow::Borrowed(f.name.as_deref().unwrap_or("")),
101 id: f.ty.id,
102 })
103 .collect(),
104 })
105 }
106
107 fn get_signature_info(
108 &self,
109 ) -> Result<ExtrinsicSignatureInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
110 Ok(ExtrinsicSignatureInfo {
111 address_id: self.extrinsic().address_ty(),
112 signature_id: self.extrinsic().signature_ty(),
113 })
114 }
115
116 fn get_extension_info(
117 &self,
118 extension_version: Option<u8>,
119 ) -> Result<ExtrinsicExtensionInfo<'_, Self::TypeId>, ExtrinsicInfoError<'_>> {
120 if let Some(extension_version) = extension_version {
124 if extension_version != 0 {
125 return Err(ExtrinsicInfoError::ExtrinsicExtensionVersionNotSupported {
126 extension_version,
127 });
128 }
129 }
130
131 Ok(ExtrinsicExtensionInfo {
132 extension_ids: self
133 .extrinsic()
134 .transaction_extensions()
135 .iter()
136 .map(|f| ExtrinsicInfoArg {
137 name: Cow::Borrowed(f.identifier()),
138 id: f.extra_ty(),
139 })
140 .collect(),
141 })
142 }
143}
144
145impl Metadata {
146 pub fn types(&self) -> &PortableRegistry {
148 &self.types
149 }
150
151 pub fn types_mut(&mut self) -> &mut PortableRegistry {
153 &mut self.types
154 }
155
156 pub fn runtime_ty(&self) -> u32 {
158 self.runtime_ty
159 }
160
161 pub fn dispatch_error_ty(&self) -> Option<u32> {
163 self.dispatch_error_ty
164 }
165
166 pub fn extrinsic(&self) -> &ExtrinsicMetadata {
168 &self.extrinsic
169 }
170
171 pub fn outer_enums(&self) -> OuterEnumsMetadata {
173 self.outer_enums
174 }
175
176 pub fn pallets(&self) -> impl ExactSizeIterator<Item = PalletMetadata<'_>> {
178 self.pallets.values().iter().map(|inner| PalletMetadata {
179 inner,
180 types: self.types(),
181 })
182 }
183
184 pub fn pallet_by_index(&self, variant_index: u8) -> Option<PalletMetadata<'_>> {
186 let inner = self
187 .pallets_by_index
188 .get(&variant_index)
189 .and_then(|i| self.pallets.get_by_index(*i))?;
190
191 Some(PalletMetadata {
192 inner,
193 types: self.types(),
194 })
195 }
196
197 pub fn pallet_by_name(&self, pallet_name: &str) -> Option<PalletMetadata<'_>> {
199 let inner = self.pallets.get_by_key(pallet_name)?;
200
201 Some(PalletMetadata {
202 inner,
203 types: self.types(),
204 })
205 }
206
207 pub fn runtime_api_traits(&self) -> impl ExactSizeIterator<Item = RuntimeApiMetadata<'_>> {
209 self.apis.values().iter().map(|inner| RuntimeApiMetadata {
210 inner,
211 types: self.types(),
212 })
213 }
214
215 pub fn runtime_api_trait_by_name(&'_ self, name: &str) -> Option<RuntimeApiMetadata<'_>> {
217 let inner = self.apis.get_by_key(name)?;
218 Some(RuntimeApiMetadata {
219 inner,
220 types: self.types(),
221 })
222 }
223
224 pub fn custom(&self) -> CustomMetadata<'_> {
226 CustomMetadata {
227 types: self.types(),
228 inner: &self.custom,
229 }
230 }
231
232 pub fn hasher(&self) -> MetadataHasher {
234 MetadataHasher::new(self)
235 }
236
237 pub fn retain<F, G>(&mut self, pallet_filter: F, api_filter: G)
242 where
243 F: FnMut(&str) -> bool,
244 G: FnMut(&str) -> bool,
245 {
246 utils::retain::retain_metadata(self, pallet_filter, api_filter);
247 }
248
249 pub fn type_hash(&self, id: u32) -> Option<[u8; HASH_LEN]> {
251 self.types.resolve(id)?;
252 Some(crate::utils::validation::get_type_hash(
253 &self.types,
254 id,
255 &OuterEnumHashes::empty(),
256 ))
257 }
258}
259
260#[derive(Debug, Clone, Copy)]
262pub struct PalletMetadata<'a> {
263 inner: &'a PalletMetadataInner,
264 types: &'a PortableRegistry,
265}
266
267impl<'a> PalletMetadata<'a> {
268 pub fn name(&self) -> &'a str {
270 &self.inner.name
271 }
272
273 pub fn index(&self) -> u8 {
275 self.inner.index
276 }
277
278 pub fn docs(&self) -> &'a [String] {
280 &self.inner.docs
281 }
282
283 pub fn call_ty_id(&self) -> Option<u32> {
285 self.inner.call_ty
286 }
287
288 pub fn event_ty_id(&self) -> Option<u32> {
290 self.inner.event_ty
291 }
292
293 pub fn error_ty_id(&self) -> Option<u32> {
295 self.inner.error_ty
296 }
297
298 pub fn storage(&self) -> Option<&'a StorageMetadata> {
300 self.inner.storage.as_ref()
301 }
302
303 pub fn event_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
305 VariantIndex::get(self.inner.event_ty, self.types)
306 }
307
308 pub fn event_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
310 self.inner.event_variant_index.lookup_by_index(
311 variant_index,
312 self.inner.event_ty,
313 self.types,
314 )
315 }
316
317 pub fn call_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
319 VariantIndex::get(self.inner.call_ty, self.types)
320 }
321
322 pub fn call_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
324 self.inner
325 .call_variant_index
326 .lookup_by_index(variant_index, self.inner.call_ty, self.types)
327 }
328
329 pub fn call_variant_by_name(&self, call_name: &str) -> Option<&'a Variant<PortableForm>> {
331 self.inner
332 .call_variant_index
333 .lookup_by_name(call_name, self.inner.call_ty, self.types)
334 }
335
336 pub fn error_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
338 VariantIndex::get(self.inner.error_ty, self.types)
339 }
340
341 pub fn error_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
343 self.inner.error_variant_index.lookup_by_index(
344 variant_index,
345 self.inner.error_ty,
346 self.types,
347 )
348 }
349
350 pub fn constant_by_name(&self, name: &str) -> Option<&'a ConstantMetadata> {
352 self.inner.constants.get_by_key(name)
353 }
354
355 pub fn constants(&self) -> impl ExactSizeIterator<Item = &'a ConstantMetadata> {
357 self.inner.constants.values().iter()
358 }
359
360 pub fn storage_hash(&self, entry_name: &str) -> Option<[u8; HASH_LEN]> {
362 crate::utils::validation::get_storage_hash(self, entry_name)
363 }
364
365 pub fn constant_hash(&self, constant_name: &str) -> Option<[u8; HASH_LEN]> {
367 crate::utils::validation::get_constant_hash(self, constant_name)
368 }
369
370 pub fn call_hash(&self, call_name: &str) -> Option<[u8; HASH_LEN]> {
372 crate::utils::validation::get_call_hash(self, call_name)
373 }
374
375 pub fn hash(&self) -> [u8; HASH_LEN] {
377 crate::utils::validation::get_pallet_hash(*self, &OuterEnumHashes::empty())
378 }
379}
380
381#[derive(Debug, Clone)]
382struct PalletMetadataInner {
383 name: ArcStr,
385 index: u8,
387 storage: Option<StorageMetadata>,
389 call_ty: Option<u32>,
391 call_variant_index: VariantIndex,
393 event_ty: Option<u32>,
395 event_variant_index: VariantIndex,
397 error_ty: Option<u32>,
399 error_variant_index: VariantIndex,
401 constants: OrderedMap<ArcStr, ConstantMetadata>,
403 docs: Vec<String>,
405}
406
407#[derive(Debug, Clone)]
409pub struct StorageMetadata {
410 prefix: String,
412 entries: OrderedMap<ArcStr, StorageEntryMetadata>,
414}
415
416impl StorageMetadata {
417 pub fn prefix(&self) -> &str {
419 &self.prefix
420 }
421
422 pub fn entries(&self) -> &[StorageEntryMetadata] {
424 self.entries.values()
425 }
426
427 pub fn entry_by_name(&self, name: &str) -> Option<&StorageEntryMetadata> {
429 self.entries.get_by_key(name)
430 }
431}
432
433#[derive(Debug, Clone)]
435pub struct StorageEntryMetadata {
436 name: ArcStr,
438 modifier: StorageEntryModifier,
440 entry_type: StorageEntryType,
442 default: Vec<u8>,
444 docs: Vec<String>,
446}
447
448impl StorageEntryMetadata {
449 pub fn name(&self) -> &str {
451 &self.name
452 }
453 pub fn modifier(&self) -> StorageEntryModifier {
455 self.modifier
456 }
457 pub fn entry_type(&self) -> &StorageEntryType {
459 &self.entry_type
460 }
461 pub fn default_bytes(&self) -> &[u8] {
463 &self.default
464 }
465 pub fn docs(&self) -> &[String] {
467 &self.docs
468 }
469}
470
471#[derive(Debug, Clone)]
473pub enum StorageEntryType {
474 Plain(u32),
476 Map {
478 hashers: Vec<StorageHasher>,
480 key_ty: u32,
482 value_ty: u32,
484 },
485}
486
487impl StorageEntryType {
488 pub fn value_ty(&self) -> u32 {
490 match self {
491 StorageEntryType::Map { value_ty, .. } | StorageEntryType::Plain(value_ty) => *value_ty,
492 }
493 }
494
495 pub fn key_ty(&self) -> Option<u32> {
497 match self {
498 StorageEntryType::Map { key_ty, .. } => Some(*key_ty),
499 StorageEntryType::Plain(_) => None,
500 }
501 }
502}
503
504#[derive(Debug, Clone, Copy)]
506pub enum StorageHasher {
507 Blake2_128,
509 Blake2_256,
511 Blake2_128Concat,
513 Twox128,
515 Twox256,
517 Twox64Concat,
519 Identity,
521}
522
523impl StorageHasher {
524 pub fn len_excluding_key(&self) -> usize {
532 match self {
533 StorageHasher::Blake2_128Concat => 16,
534 StorageHasher::Twox64Concat => 8,
535 StorageHasher::Blake2_128 => 16,
536 StorageHasher::Blake2_256 => 32,
537 StorageHasher::Twox128 => 16,
538 StorageHasher::Twox256 => 32,
539 StorageHasher::Identity => 0,
540 }
541 }
542
543 pub fn ends_with_key(&self) -> bool {
545 matches!(
546 self,
547 StorageHasher::Blake2_128Concat | StorageHasher::Twox64Concat | StorageHasher::Identity
548 )
549 }
550}
551
552#[derive(Debug, Clone, Copy, Eq, PartialEq)]
554pub enum StorageEntryModifier {
555 Optional,
557 Default,
559}
560
561#[derive(Debug, Clone)]
563pub struct ConstantMetadata {
564 name: ArcStr,
566 ty: u32,
568 value: Vec<u8>,
570 docs: Vec<String>,
572}
573
574impl ConstantMetadata {
575 pub fn name(&self) -> &str {
577 &self.name
578 }
579 pub fn ty(&self) -> u32 {
581 self.ty
582 }
583 pub fn value(&self) -> &[u8] {
585 &self.value
586 }
587 pub fn docs(&self) -> &[String] {
589 &self.docs
590 }
591}
592
593#[derive(Debug, Clone)]
595pub struct ExtrinsicMetadata {
596 address_ty: u32,
598 call_ty: u32,
600 signature_ty: u32,
602 extra_ty: u32,
604 supported_versions: Vec<u8>,
606 transaction_extensions: Vec<TransactionExtensionMetadata>,
608 transaction_extensions_version: u8,
612}
613
614impl ExtrinsicMetadata {
615 pub fn address_ty(&self) -> u32 {
617 self.address_ty
618 }
619
620 pub fn call_ty(&self) -> u32 {
622 self.call_ty
623 }
624 pub fn signature_ty(&self) -> u32 {
626 self.signature_ty
627 }
628 pub fn extra_ty(&self) -> u32 {
630 self.extra_ty
631 }
632
633 pub fn supported_versions(&self) -> &[u8] {
635 &self.supported_versions
636 }
637
638 pub fn transaction_extensions(&self) -> &[TransactionExtensionMetadata] {
640 &self.transaction_extensions
641 }
642
643 pub fn transaction_extensions_version(&self) -> u8 {
645 self.transaction_extensions_version
646 }
647}
648
649#[derive(Debug, Clone)]
651pub struct TransactionExtensionMetadata {
652 identifier: String,
654 extra_ty: u32,
656 additional_ty: u32,
658}
659
660impl TransactionExtensionMetadata {
661 pub fn identifier(&self) -> &str {
663 &self.identifier
664 }
665 pub fn extra_ty(&self) -> u32 {
667 self.extra_ty
668 }
669 pub fn additional_ty(&self) -> u32 {
671 self.additional_ty
672 }
673}
674
675#[derive(Debug, Clone, Copy)]
677pub struct OuterEnumsMetadata {
678 call_enum_ty: u32,
680 event_enum_ty: u32,
682 error_enum_ty: u32,
684}
685
686impl OuterEnumsMetadata {
687 pub fn call_enum_ty(&self) -> u32 {
689 self.call_enum_ty
690 }
691
692 pub fn event_enum_ty(&self) -> u32 {
694 self.event_enum_ty
695 }
696
697 pub fn error_enum_ty(&self) -> u32 {
699 self.error_enum_ty
700 }
701}
702
703#[derive(Debug, Clone, Copy)]
705pub struct RuntimeApiMetadata<'a> {
706 inner: &'a RuntimeApiMetadataInner,
707 types: &'a PortableRegistry,
708}
709
710impl<'a> RuntimeApiMetadata<'a> {
711 pub fn name(&self) -> &'a str {
713 &self.inner.name
714 }
715 pub fn docs(&self) -> &[String] {
717 &self.inner.docs
718 }
719 pub fn methods(&self) -> impl ExactSizeIterator<Item = &'a RuntimeApiMethodMetadata> {
721 self.inner.methods.values().iter()
722 }
723 pub fn method_by_name(&self, name: &str) -> Option<&'a RuntimeApiMethodMetadata> {
725 self.inner.methods.get_by_key(name)
726 }
727 pub fn method_hash(&self, method_name: &str) -> Option<[u8; HASH_LEN]> {
729 crate::utils::validation::get_runtime_api_hash(self, method_name)
730 }
731
732 pub fn hash(&self) -> [u8; HASH_LEN] {
734 crate::utils::validation::get_runtime_trait_hash(*self, &OuterEnumHashes::empty())
735 }
736}
737
738#[derive(Debug, Clone)]
739struct RuntimeApiMetadataInner {
740 name: ArcStr,
742 methods: OrderedMap<ArcStr, RuntimeApiMethodMetadata>,
744 docs: Vec<String>,
746}
747
748#[derive(Debug, Clone)]
750pub struct RuntimeApiMethodMetadata {
751 name: ArcStr,
753 inputs: Vec<RuntimeApiMethodParamMetadata>,
755 output_ty: u32,
757 docs: Vec<String>,
759}
760
761impl RuntimeApiMethodMetadata {
762 pub fn name(&self) -> &str {
764 &self.name
765 }
766 pub fn docs(&self) -> &[String] {
768 &self.docs
769 }
770 pub fn inputs(&self) -> impl ExactSizeIterator<Item = &RuntimeApiMethodParamMetadata> {
772 self.inputs.iter()
773 }
774 pub fn output_ty(&self) -> u32 {
776 self.output_ty
777 }
778}
779
780#[derive(Debug, Clone)]
782pub struct RuntimeApiMethodParamMetadata {
783 pub name: String,
785 pub ty: u32,
787}
788
789#[derive(Debug, Clone)]
791pub struct CustomMetadata<'a> {
792 types: &'a PortableRegistry,
793 inner: &'a frame_metadata::v15::CustomMetadata<PortableForm>,
794}
795
796impl<'a> CustomMetadata<'a> {
797 pub fn get(&self, name: &str) -> Option<CustomValueMetadata<'a>> {
799 self.inner
800 .map
801 .get_key_value(name)
802 .map(|(name, e)| CustomValueMetadata {
803 types: self.types,
804 type_id: e.ty.id,
805 data: &e.value,
806 name,
807 })
808 }
809
810 pub fn iter(&self) -> impl Iterator<Item = CustomValueMetadata> {
812 self.inner.map.iter().map(|(name, e)| CustomValueMetadata {
813 types: self.types,
814 type_id: e.ty.id,
815 data: &e.value,
816 name: name.as_ref(),
817 })
818 }
819
820 pub fn types(&self) -> &PortableRegistry {
822 self.types
823 }
824}
825
826pub struct CustomValueMetadata<'a> {
828 types: &'a PortableRegistry,
829 type_id: u32,
830 data: &'a [u8],
831 name: &'a str,
832}
833
834impl<'a> CustomValueMetadata<'a> {
835 pub fn types(&self) -> &PortableRegistry {
837 self.types
838 }
839
840 pub fn bytes(&self) -> &'a [u8] {
842 self.data
843 }
844
845 pub fn type_id(&self) -> u32 {
847 self.type_id
848 }
849
850 pub fn name(&self) -> &str {
852 self.name
853 }
854
855 pub fn hash(&self) -> [u8; HASH_LEN] {
857 get_custom_value_hash(self, &OuterEnumHashes::empty())
858 }
859}
860
861impl codec::Decode for Metadata {
864 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
865 let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(input)?;
866 let metadata = match metadata.1 {
867 frame_metadata::RuntimeMetadata::V14(md) => md.try_into(),
868 frame_metadata::RuntimeMetadata::V15(md) => md.try_into(),
869 _ => return Err("Cannot try_into() to Metadata: unsupported metadata version".into()),
870 };
871
872 metadata.map_err(|_e| "Cannot try_into() to Metadata.".into())
873 }
874}
875
876impl codec::Encode for Metadata {
880 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
881 let m: frame_metadata::v15::RuntimeMetadataV15 = self.clone().into();
882 let m: frame_metadata::RuntimeMetadataPrefixed = m.into();
883 m.encode_to(dest)
884 }
885}
886
887#[cfg(test)]
888mod test {
889 use super::*;
890 use codec::{Decode, Encode};
891
892 fn load_metadata() -> Vec<u8> {
893 std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap()
894 }
895
896 #[test]
900 fn is_isomorphic_to_v15() {
901 let bytes = load_metadata();
902
903 let metadata = Metadata::decode(&mut &*bytes).unwrap();
905
906 let v15: frame_metadata::v15::RuntimeMetadataV15 = metadata.into();
908 let prefixed = frame_metadata::RuntimeMetadataPrefixed::from(v15);
909
910 let new_bytes = prefixed.encode();
912
913 assert_eq!(bytes, new_bytes);
915 }
916}