polymesh_api_client/metadata/
storage.rs

1use super::*;
2use sp_core::hashing::{blake2_128, blake2_256, twox_128, twox_256, twox_64};
3
4/// Metadata for a pallet's storage.
5///
6/// Contains information about the storage prefix and all storage entries in this pallet.
7#[derive(Clone)]
8pub struct StorageMetadata {
9  /// The prefix used for all storage items in this pallet.
10  pub prefix: String,
11  /// The storage entries in this pallet, keyed by entry name.
12  pub entries: BTreeMap<String, StorageEntryMetadata>,
13}
14
15impl StorageMetadata {
16  /// Creates storage metadata from V12 metadata format.
17  ///
18  /// # Arguments
19  ///
20  /// * `md` - The V12 storage metadata
21  /// * `lookup` - Types registry for resolving type references
22  ///
23  /// # Returns
24  ///
25  /// The parsed storage metadata, or an error if parsing fails.
26  #[cfg(feature = "v12")]
27  pub fn from_v12_meta(
28    md: frame_metadata::v12::StorageMetadata,
29    lookup: &mut Types,
30  ) -> Result<Self> {
31    let prefix = decode_meta(&md.prefix)?.clone();
32    let mut entries = BTreeMap::new();
33
34    decode_meta(&md.entries)?
35      .iter()
36      .try_for_each(|entry| -> Result<()> {
37        let entry_md = StorageEntryMetadata::from_v12_meta(entry, lookup)?;
38        let name = entry_md.name.clone();
39        entries.insert(name, entry_md);
40        Ok(())
41      })?;
42
43    Ok(Self { prefix, entries })
44  }
45
46  /// Creates storage metadata from V13 metadata format.
47  ///
48  /// # Arguments
49  ///
50  /// * `md` - The V13 storage metadata
51  /// * `lookup` - Types registry for resolving type references
52  ///
53  /// # Returns
54  ///
55  /// The parsed storage metadata, or an error if parsing fails.
56  #[cfg(feature = "v13")]
57  pub fn from_v13_meta(
58    md: frame_metadata::v13::StorageMetadata,
59    lookup: &mut Types,
60  ) -> Result<Self> {
61    let prefix = decode_meta(&md.prefix)?.clone();
62    let mut entries = BTreeMap::new();
63
64    decode_meta(&md.entries)?
65      .iter()
66      .try_for_each(|entry| -> Result<()> {
67        let entry_md = StorageEntryMetadata::from_v13_meta(entry, lookup)?;
68        let name = entry_md.name.clone();
69        entries.insert(name, entry_md);
70        Ok(())
71      })?;
72
73    Ok(Self { prefix, entries })
74  }
75
76  /// Creates storage metadata from V14 metadata format.
77  ///
78  /// # Arguments
79  ///
80  /// * `md` - The V14 pallet storage metadata
81  /// * `types` - Registry of portable types for resolving type references
82  ///
83  /// # Returns
84  ///
85  /// The parsed storage metadata, or an error if parsing fails.
86  #[cfg(feature = "v14")]
87  pub fn from_v14_meta(
88    md: &frame_metadata::v14::PalletStorageMetadata<PortableForm>,
89    types: &PortableRegistry,
90  ) -> Result<Self> {
91    let prefix = md.prefix.clone();
92    let mut entries = BTreeMap::new();
93
94    md.entries.iter().try_for_each(|entry| -> Result<()> {
95      let entry_md = StorageEntryMetadata::from_v14_meta(entry, types)?;
96      let name = entry_md.name.clone();
97      entries.insert(name, entry_md);
98      Ok(())
99    })?;
100
101    Ok(Self { prefix, entries })
102  }
103
104  /// Computes the pallet prefix hash, which is the xxhash128 of the pallet's storage prefix.
105  ///
106  /// # Returns
107  ///
108  /// The xxhash128 of the pallet prefix as a vector of bytes.
109  pub fn pallet_prefix_hash(&self) -> Vec<u8> {
110    twox_128(self.prefix.as_bytes()).to_vec()
111  }
112
113  /// Computes the storage prefix hash for a given entry, which is the pallet prefix hash
114  /// followed by the xxhash128 of the entry name.
115  ///
116  /// # Arguments
117  ///
118  /// * `entry_name` - The name of the storage entry
119  ///
120  /// # Returns
121  ///
122  /// The complete storage prefix hash as a vector of bytes.
123  pub fn storage_prefix_hash(&self, entry_name: &str) -> Result<Vec<u8>> {
124    let entry = self.entries.get(entry_name).ok_or_else(|| {
125      Error::StorageKeyGenerationFailed(format!("Storage entry '{}' not found", entry_name))
126    })?;
127    Ok(entry.entry_prefix_hash(&self.pallet_prefix_hash()))
128  }
129
130  /// Computes the full storage key for a given entry, given its keys (if any).
131  /// This is a convenience method that delegates to the corresponding `StorageEntryMetadata`.
132  ///
133  /// # Arguments
134  ///
135  /// * `entry_name` - The name of the storage entry
136  /// * `keys` - The keys for this storage entry, if it's a map
137  ///
138  /// # Returns
139  ///
140  /// The complete storage key as a vector of bytes, or an error if the entry doesn't exist or
141  /// the provided keys don't match the storage entry type.
142  pub fn storage_key(&self, entry_name: &str, keys: &[Vec<u8>]) -> Result<Vec<u8>> {
143    let entry = self.entries.get(entry_name).ok_or_else(|| {
144      Error::StorageKeyGenerationFailed(format!("Storage entry '{}' not found", entry_name))
145    })?;
146    entry.storage_key(&self.pallet_prefix_hash(), keys)
147  }
148}
149
150/// The hashing algorithm used for generating storage keys.
151#[derive(Clone)]
152pub enum StorageHasher {
153  /// Blake2 128-bit hash.
154  Blake2_128,
155  /// Blake2 256-bit hash.
156  Blake2_256,
157  /// Blake2 128-bit hash followed by the input data.
158  Blake2_128Concat,
159  /// XX 128-bit hash.
160  Twox128,
161  /// XX 256-bit hash.
162  Twox256,
163  /// XX 64-bit hash followed by the input data.
164  Twox64Concat,
165  /// Identity hashing (no hashing, data used as-is).
166  Identity,
167}
168
169impl StorageHasher {
170  /// Apply this hasher to the given data.
171  ///
172  /// # Arguments
173  ///
174  /// * `data` - The input data to hash
175  ///
176  /// # Returns
177  ///
178  /// The hashed data as a vector of bytes.
179  pub fn hash_data(&self, data: &[u8]) -> Vec<u8> {
180    match self {
181      Self::Blake2_128 => blake2_128(data).to_vec(),
182      Self::Blake2_256 => blake2_256(data).to_vec(),
183      Self::Blake2_128Concat => {
184        let mut result = blake2_128(data).to_vec();
185        result.extend_from_slice(data);
186        result
187      }
188      Self::Twox128 => twox_128(data).to_vec(),
189      Self::Twox256 => twox_256(data).to_vec(),
190      Self::Twox64Concat => {
191        let mut result = twox_64(data).to_vec();
192        result.extend_from_slice(data);
193        result
194      }
195      Self::Identity => data.to_vec(),
196    }
197  }
198}
199
200#[cfg(feature = "v12")]
201impl From<&frame_metadata::v12::StorageHasher> for StorageHasher {
202  fn from(hasher: &frame_metadata::v12::StorageHasher) -> Self {
203    use frame_metadata::v12::StorageHasher as MetadataHasher;
204    match hasher {
205      MetadataHasher::Blake2_128 => Self::Blake2_128,
206      MetadataHasher::Blake2_256 => Self::Blake2_256,
207      MetadataHasher::Blake2_128Concat => Self::Blake2_128Concat,
208      MetadataHasher::Twox128 => Self::Twox128,
209      MetadataHasher::Twox256 => Self::Twox256,
210      MetadataHasher::Twox64Concat => Self::Twox64Concat,
211      MetadataHasher::Identity => Self::Identity,
212    }
213  }
214}
215
216#[cfg(feature = "v13")]
217impl From<&frame_metadata::v13::StorageHasher> for StorageHasher {
218  fn from(hasher: &frame_metadata::v13::StorageHasher) -> Self {
219    use frame_metadata::v13::StorageHasher as MetadataHasher;
220    match hasher {
221      MetadataHasher::Blake2_128 => Self::Blake2_128,
222      MetadataHasher::Blake2_256 => Self::Blake2_256,
223      MetadataHasher::Blake2_128Concat => Self::Blake2_128Concat,
224      MetadataHasher::Twox128 => Self::Twox128,
225      MetadataHasher::Twox256 => Self::Twox256,
226      MetadataHasher::Twox64Concat => Self::Twox64Concat,
227      MetadataHasher::Identity => Self::Identity,
228    }
229  }
230}
231
232#[cfg(feature = "v14")]
233impl From<&frame_metadata::v14::StorageHasher> for StorageHasher {
234  fn from(hasher: &frame_metadata::v14::StorageHasher) -> Self {
235    use frame_metadata::v14::StorageHasher as MetadataHasher;
236    match hasher {
237      MetadataHasher::Blake2_128 => Self::Blake2_128,
238      MetadataHasher::Blake2_256 => Self::Blake2_256,
239      MetadataHasher::Blake2_128Concat => Self::Blake2_128Concat,
240      MetadataHasher::Twox128 => Self::Twox128,
241      MetadataHasher::Twox256 => Self::Twox256,
242      MetadataHasher::Twox64Concat => Self::Twox64Concat,
243      MetadataHasher::Identity => Self::Identity,
244    }
245  }
246}
247
248/// Type information for a storage entry.
249///
250/// Represents either a plain storage entry (single value) or a map with one or more keys.
251#[derive(Clone)]
252pub enum StorageEntryType {
253  /// A simple storage entry with a single value of the given type.
254  Plain(TypeId),
255  /// A storage map from keys to values.
256  Map {
257    /// The hashing algorithm used for the first key.
258    hasher: StorageHasher,
259    /// The type ID of the first key.
260    key: TypeId,
261    /// The type ID of the value.
262    value: TypeId,
263    /// For NMaps (double maps or higher), contains pairs of (hasher, key_type)
264    /// for additional keys beyond the first one.
265    additional_hashers_keys: Vec<(StorageHasher, TypeId)>,
266  },
267}
268
269/// Modifier for a storage entry that indicates how the entry behaves when not set.
270#[derive(Clone)]
271pub enum StorageEntryModifier {
272  /// If the entry doesn't exist, it's reported as `None`.
273  Optional,
274  /// If the entry doesn't exist, the default value is returned.
275  Default,
276}
277
278#[cfg(feature = "v12")]
279impl From<&frame_metadata::v12::StorageEntryModifier> for StorageEntryModifier {
280  fn from(modifier: &frame_metadata::v12::StorageEntryModifier) -> Self {
281    use frame_metadata::v12::StorageEntryModifier as MetadataModifier;
282    match modifier {
283      MetadataModifier::Optional => Self::Optional,
284      MetadataModifier::Default => Self::Default,
285    }
286  }
287}
288
289#[cfg(feature = "v13")]
290impl From<&frame_metadata::v13::StorageEntryModifier> for StorageEntryModifier {
291  fn from(modifier: &frame_metadata::v13::StorageEntryModifier) -> Self {
292    use frame_metadata::v13::StorageEntryModifier as MetadataModifier;
293    match modifier {
294      MetadataModifier::Optional => Self::Optional,
295      MetadataModifier::Default => Self::Default,
296    }
297  }
298}
299
300#[cfg(feature = "v14")]
301impl From<&frame_metadata::v14::StorageEntryModifier> for StorageEntryModifier {
302  fn from(modifier: &frame_metadata::v14::StorageEntryModifier) -> Self {
303    use frame_metadata::v14::StorageEntryModifier as MetadataModifier;
304    match modifier {
305      MetadataModifier::Optional => Self::Optional,
306      MetadataModifier::Default => Self::Default,
307    }
308  }
309}
310
311/// Metadata for a single storage entry within a pallet.
312#[derive(Clone)]
313pub struct StorageEntryMetadata {
314  /// The name of the storage entry.
315  pub name: String,
316  /// The modifier indicating behavior when the entry doesn't exist.
317  pub modifier: StorageEntryModifier,
318  /// The type information for this storage entry.
319  pub ty: StorageEntryType,
320  /// The default value for this entry as SCALE-encoded bytes.
321  pub default: Vec<u8>,
322  /// Documentation for this storage entry.
323  pub docs: Docs,
324}
325
326impl StorageEntryMetadata {
327  /// Creates storage entry metadata from V12 metadata format.
328  ///
329  /// # Arguments
330  ///
331  /// * `md` - The V12 storage entry metadata
332  /// * `lookup` - Types registry for resolving type references
333  ///
334  /// # Returns
335  ///
336  /// The parsed storage entry metadata, or an error if parsing fails.
337  #[cfg(feature = "v12")]
338  fn from_v12_meta(
339    md: &frame_metadata::v12::StorageEntryMetadata,
340    lookup: &mut Types,
341  ) -> Result<Self> {
342    let name = decode_meta(&md.name)?.clone();
343    let modifier = (&md.modifier).into();
344    let docs = Docs::from_v12_meta(&md.documentation)?;
345    let default = decode_meta(&md.default)?.clone();
346
347    let ty = match &md.ty {
348      frame_metadata::v12::StorageEntryType::Plain(plain_ty) => {
349        let ty_name = decode_meta(plain_ty)?;
350        let ty_id = lookup.parse_type(&ty_name)?;
351        StorageEntryType::Plain(ty_id)
352      }
353      frame_metadata::v12::StorageEntryType::Map {
354        hasher, key, value, ..
355      } => {
356        let key_ty = decode_meta(key)?;
357        let value_ty = decode_meta(value)?;
358        let key_id = lookup.parse_type(&key_ty)?;
359        let value_id = lookup.parse_type(&value_ty)?;
360        let hasher = hasher.into();
361
362        StorageEntryType::Map {
363          hasher,
364          key: key_id,
365          value: value_id,
366          additional_hashers_keys: Vec::new(),
367        }
368      }
369      frame_metadata::v12::StorageEntryType::DoubleMap {
370        hasher,
371        key1,
372        key2,
373        value,
374        key2_hasher,
375      } => {
376        let key1_ty = decode_meta(key1)?;
377        let key2_ty = decode_meta(key2)?;
378        let value_ty = decode_meta(value)?;
379        let key1_id = lookup.parse_type(&key1_ty)?;
380        let key2_id = lookup.parse_type(&key2_ty)?;
381        let value_id = lookup.parse_type(&value_ty)?;
382        let hasher1 = (hasher).into();
383        let hasher2 = (key2_hasher).into();
384
385        StorageEntryType::Map {
386          hasher: hasher1,
387          key: key1_id,
388          value: value_id,
389          additional_hashers_keys: vec![(hasher2, key2_id)],
390        }
391      }
392    };
393
394    Ok(Self {
395      name,
396      modifier,
397      ty,
398      default,
399      docs,
400    })
401  }
402
403  /// Creates storage entry metadata from V13 metadata format.
404  ///
405  /// # Arguments
406  ///
407  /// * `md` - The V13 storage entry metadata
408  /// * `lookup` - Types registry for resolving type references
409  ///
410  /// # Returns
411  ///
412  /// The parsed storage entry metadata, or an error if parsing fails.
413  #[cfg(feature = "v13")]
414  fn from_v13_meta(
415    md: &frame_metadata::v13::StorageEntryMetadata,
416    lookup: &mut Types,
417  ) -> Result<Self> {
418    let name = decode_meta(&md.name)?.clone();
419    let modifier = (&md.modifier).into();
420    let docs = Docs::from_v13_meta(&md.documentation)?;
421    let default = decode_meta(&md.default)?.clone();
422
423    let ty = match &md.ty {
424      frame_metadata::v13::StorageEntryType::Plain(plain_ty) => {
425        let ty_name = decode_meta(plain_ty)?;
426        let ty_id = lookup.parse_type(&ty_name)?;
427        StorageEntryType::Plain(ty_id)
428      }
429      frame_metadata::v13::StorageEntryType::Map {
430        hasher, key, value, ..
431      } => {
432        let key_ty = decode_meta(key)?;
433        let value_ty = decode_meta(value)?;
434        let key_id = lookup.parse_type(&key_ty)?;
435        let value_id = lookup.parse_type(&value_ty)?;
436        let hasher = (hasher).into();
437
438        StorageEntryType::Map {
439          hasher,
440          key: key_id,
441          value: value_id,
442          additional_hashers_keys: Vec::new(),
443        }
444      }
445      frame_metadata::v13::StorageEntryType::DoubleMap {
446        hasher,
447        key1,
448        key2,
449        value,
450        key2_hasher,
451      } => {
452        let key1_ty = decode_meta(key1)?;
453        let key2_ty = decode_meta(key2)?;
454        let value_ty = decode_meta(value)?;
455        let key1_id = lookup.parse_type(&key1_ty)?;
456        let key2_id = lookup.parse_type(&key2_ty)?;
457        let value_id = lookup.parse_type(&value_ty)?;
458        let hasher1 = (hasher).into();
459        let hasher2 = (key2_hasher).into();
460
461        StorageEntryType::Map {
462          hasher: hasher1,
463          key: key1_id,
464          value: value_id,
465          additional_hashers_keys: vec![(hasher2, key2_id)],
466        }
467      }
468      frame_metadata::v13::StorageEntryType::NMap {
469        hashers,
470        keys,
471        value,
472      } => {
473        let keys_ty = decode_meta(keys)?;
474        let value_ty = decode_meta(value)?;
475        let hashers_vec = decode_meta(hashers)?;
476
477        // Process first key and hasher
478        let first_key_ty = &keys_ty[0];
479        let first_key_id = lookup.parse_type(first_key_ty)?;
480        let first_hasher = (&hashers_vec[0]).into();
481
482        // Process additional keys and hashers
483        let mut additional_hashers_keys = Vec::new();
484        for i in 1..keys_ty.len() {
485          let key_ty = &keys_ty[i];
486          let key_id = lookup.parse_type(key_ty)?;
487          let hasher = (&hashers_vec[i]).into();
488          additional_hashers_keys.push((hasher, key_id));
489        }
490
491        let value_id = lookup.parse_type(&value_ty)?;
492
493        StorageEntryType::Map {
494          hasher: first_hasher,
495          key: first_key_id,
496          value: value_id,
497          additional_hashers_keys,
498        }
499      }
500    };
501
502    Ok(Self {
503      name,
504      modifier,
505      ty,
506      default,
507      docs,
508    })
509  }
510
511  /// Creates storage entry metadata from V14 metadata format.
512  ///
513  /// # Arguments
514  ///
515  /// * `md` - The V14 storage entry metadata
516  /// * `types` - Registry of portable types for resolving type references
517  ///
518  /// # Returns
519  ///
520  /// The parsed storage entry metadata, or an error if parsing fails.
521  #[cfg(feature = "v14")]
522  fn from_v14_meta(
523    md: &frame_metadata::v14::StorageEntryMetadata<PortableForm>,
524    types: &PortableRegistry,
525  ) -> Result<Self> {
526    let name = md.name.clone();
527    let modifier = (&md.modifier).into();
528    let docs = Docs::from_v14_meta(&md.docs);
529    let default = md.default.clone();
530
531    let ty = match &md.ty {
532      frame_metadata::v14::StorageEntryType::Plain(plain_ty) => {
533        StorageEntryType::Plain(TypeId::from(plain_ty.id))
534      }
535      frame_metadata::v14::StorageEntryType::Map {
536        hashers,
537        key,
538        value,
539      } => {
540        if hashers.len() == 1 {
541          // Simple map
542          let hasher = (&hashers[0]).into();
543          StorageEntryType::Map {
544            hasher,
545            key: TypeId::from(key.id),
546            value: TypeId::from(value.id),
547            additional_hashers_keys: Vec::new(),
548          }
549        } else if hashers.len() > 1 {
550          // NMap (double map or higher)
551          let first_hasher = (&hashers[0]).into();
552
553          // For NMap, we need to extract the individual key types from the tuple
554          let key_type = types
555            .resolve(key.id)
556            .ok_or_else(|| Error::MetadataParseFailed("Failed to resolve NMap key type".into()))?;
557
558          let mut additional_hashers_keys = Vec::new();
559
560          if let TypeDef::Tuple(tuple) = key_type.type_def() {
561            if tuple.fields.len() != hashers.len() {
562              return Err(Error::MetadataParseFailed(
563                "Mismatch between hashers and key types count".into(),
564              ));
565            }
566
567            // Skip the first key as it's handled separately
568            for i in 1..hashers.len() {
569              let key_id = tuple.fields[i].id();
570              let hasher = (&hashers[i]).into();
571              additional_hashers_keys.push((hasher, TypeId::from(key_id)));
572            }
573
574            StorageEntryType::Map {
575              hasher: first_hasher,
576              key: TypeId::from(tuple.fields[0].id()),
577              value: TypeId::from(value.id),
578              additional_hashers_keys,
579            }
580          } else {
581            return Err(Error::MetadataParseFailed(
582              "Expected tuple type for NMap keys".into(),
583            ));
584          }
585        } else {
586          return Err(Error::MetadataParseFailed(
587            "Empty hashers list in map storage entry".into(),
588          ));
589        }
590      }
591    };
592
593    Ok(Self {
594      name,
595      modifier,
596      ty,
597      default,
598      docs,
599    })
600  }
601
602  /// Computes the entry prefix hash, which is the pallet prefix hash ++ xxhash128 of the entry name.
603  ///
604  /// # Arguments
605  ///
606  /// * `pallet_prefix_hash` - The hash of the pallet prefix
607  ///
608  /// # Returns
609  ///
610  /// The complete entry prefix hash as a vector of bytes.
611  pub fn entry_prefix_hash(&self, pallet_prefix_hash: &[u8]) -> Vec<u8> {
612    let mut result = pallet_prefix_hash.to_vec();
613    result.extend_from_slice(&twox_128(self.name.as_bytes()));
614    result
615  }
616
617  /// Computes the full storage key for this entry, given its keys (if any).
618  ///
619  /// # Arguments
620  ///
621  /// * `pallet_prefix_hash` - The hash of the pallet prefix
622  /// * `keys` - The keys for this storage entry, if it's a map
623  ///
624  /// # Returns
625  ///
626  /// The complete storage key as a vector of bytes, or an error if provided keys
627  /// don't match the storage entry type.
628  pub fn storage_key(&self, pallet_prefix_hash: &[u8], keys: &[Vec<u8>]) -> Result<Vec<u8>> {
629    // Start with the entry prefix hash (pallet_prefix_hash + entry_name_hash)
630    let mut key = self.entry_prefix_hash(pallet_prefix_hash);
631
632    match &self.ty {
633      StorageEntryType::Plain(_) => {
634        // For plain storage, no additional keys are needed
635        if !keys.is_empty() {
636          return Err(Error::StorageKeyGenerationFailed(
637            "Plain storage takes no keys".into(),
638          ));
639        }
640      }
641      StorageEntryType::Map {
642        hasher,
643        additional_hashers_keys,
644        ..
645      } => {
646        // For maps, we need exactly 1 + additional_hashers_keys.len() keys
647        let expected_keys = 1 + additional_hashers_keys.len();
648        if keys.len() != expected_keys {
649          return Err(Error::StorageKeyGenerationFailed(format!(
650            "Expected {} keys for this map, got {}",
651            expected_keys,
652            keys.len()
653          )));
654        }
655
656        // Hash first key with its hasher
657        key.extend_from_slice(&hasher.hash_data(&keys[0]));
658
659        // Hash additional keys with their respective hashers
660        for (i, (hasher, _)) in additional_hashers_keys.iter().enumerate() {
661          key.extend_from_slice(&hasher.hash_data(&keys[i + 1]));
662        }
663      }
664    }
665
666    Ok(key)
667  }
668}