frame_decode/methods/
storage_encoder.rs

1// Copyright (C) 2022-2025 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::storage_type_info::{StorageHasher, StorageInfo, StorageTypeInfo};
17use crate::methods::storage_type_info::StorageInfoError;
18use crate::utils::{EncodableValues, IntoEncodableValues};
19use alloc::vec::Vec;
20use scale_type_resolver::TypeResolver;
21
22/// An error returned trying to encode storage keys.
23#[non_exhaustive]
24#[allow(missing_docs)]
25#[derive(Debug, thiserror::Error)]
26pub enum StorageKeyEncodeError {
27    #[error("Cannot get storage info: {0}")]
28    CannotGetInfo(StorageInfoError<'static>),
29    #[error("Failed to encode storage key: {0}")]
30    EncodeError(#[from] scale_encode::Error),
31    #[error("Too many keys provided: expected at most {max_keys_expected}")]
32    TooManyKeysProvided {
33        /// The maximum number of keys that were expected.
34        max_keys_expected: usize,
35    },
36}
37
38/// Encode a storage key prefix from a pallet name and storage entry name. This prefix
39/// is the first 32 bytes of any storage key which comes from a pallet, and is essentially
40/// `twox_128(pallet_name) + twox_128(storage_entry_name)`.
41pub fn encode_storage_key_prefix(pallet_name: &str, storage_entry: &str) -> [u8; 32] {
42    let mut prefix = [0u8; 32];
43
44    let pallet_bytes = sp_crypto_hashing::twox_128(pallet_name.as_bytes());
45    let entry_bytes = sp_crypto_hashing::twox_128(storage_entry.as_bytes());
46
47    prefix[..16].copy_from_slice(&pallet_bytes);
48    prefix[16..].copy_from_slice(&entry_bytes);
49
50    prefix
51}
52
53/// Encode a complete storage key for a given pallet and storage entry and a set of keys that
54/// are each able to be encoded via [`scale_encode::EncodeAsType`].
55///
56/// This is the same as [`encode_storage_key_to`], but returns the encoded key as a `Vec<u8>`, rather
57/// than accepting a mutable output buffer.
58///
59/// # Example
60///
61/// ```rust
62/// use frame_decode::storage::encode_storage_key;
63/// use frame_metadata::RuntimeMetadata;
64/// use parity_scale_codec::Decode;
65///
66/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
67/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
68///
69/// let account_id = [0u8; 32];
70///
71/// // System.Account needs only one key to point at a specific value; the account ID.
72/// // We just fake an account ID for this example  by providing 32 0 bytes, but anything
73/// // which would `scale_encode::EncodeAsType` into 32 bytes would work.
74/// let encoded_key = encode_storage_key(
75///     "System",
76///     "Account",
77///     &[account_id],
78///     &metadata,
79///     &metadata.types,
80/// ).unwrap();
81/// ```
82pub fn encode_storage_key<Info, Resolver, Keys>(
83    pallet_name: &str,
84    storage_entry: &str,
85    keys: Keys,
86    info: &Info,
87    type_resolver: &Resolver,
88) -> Result<Vec<u8>, StorageKeyEncodeError>
89where
90    Keys: IntoEncodableValues,
91    Info: StorageTypeInfo,
92    Info::TypeId: Clone + core::fmt::Debug,
93    Resolver: TypeResolver<TypeId = Info::TypeId>,
94{
95    // pre-allocate at least as many bytes as we need for the root/prefix.
96    let mut out = Vec::with_capacity(32);
97    encode_storage_key_to(
98        pallet_name,
99        storage_entry,
100        keys,
101        info,
102        type_resolver,
103        &mut out,
104    )?;
105    Ok(out)
106}
107
108/// Encode a complete storage key for a given pallet and storage entry and a set of keys that
109/// are each able to be encoded via [`scale_encode::EncodeAsType`].
110///
111/// # Example
112///
113/// ```rust
114/// use frame_decode::storage::encode_storage_key_to;
115/// use frame_metadata::RuntimeMetadata;
116/// use parity_scale_codec::Decode;
117///
118/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
119/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
120///
121/// let account_id = [0u8; 32];
122///
123/// // We'll encode the key to this.
124/// let mut encoded_key = Vec::new();
125///
126/// // System.Account needs only one key to point at a specific value; the account ID.
127/// // We just fake an account ID for this example  by providing 32 0 bytes, but anything
128/// // which would `scale_encode::EncodeAsType` into 32 bytes would work.
129/// encode_storage_key_to(
130///     "System",
131///     "Account",
132///     &[account_id],
133///     &metadata,
134///     &metadata.types,
135///     &mut encoded_key,
136/// ).unwrap();
137/// ```
138pub fn encode_storage_key_to<Info, Resolver, Keys>(
139    pallet_name: &str,
140    storage_entry: &str,
141    keys: Keys,
142    info: &Info,
143    type_resolver: &Resolver,
144    out: &mut Vec<u8>,
145) -> Result<(), StorageKeyEncodeError>
146where
147    Keys: IntoEncodableValues,
148    Info: StorageTypeInfo,
149    Info::TypeId: Clone + core::fmt::Debug,
150    Resolver: TypeResolver<TypeId = Info::TypeId>,
151{
152    let storage_info = info
153        .storage_info(pallet_name, storage_entry)
154        .map_err(|e| StorageKeyEncodeError::CannotGetInfo(e.into_owned()))?;
155
156    encode_storage_key_with_info_to(
157        pallet_name,
158        storage_entry,
159        keys,
160        &storage_info,
161        type_resolver,
162        out,
163    )
164}
165
166/// Encode a complete storage key for a given pallet and storage entry and a set of keys that
167/// are each able to be encoded via [`scale_encode::EncodeAsType`].
168///
169/// Unlike [`encode_storage_key`], which obtains the storage info internally given the pallet and storage entry names,
170/// this function takes the storage info as an argument. This is useful if you already have the storage info available,
171/// for example if you are encoding multiple keys for the same storage entry.
172pub fn encode_storage_key_with_info<Resolver, Keys>(
173    pallet_name: &str,
174    storage_entry: &str,
175    keys: Keys,
176    storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
177    type_resolver: &Resolver,
178) -> Result<Vec<u8>, StorageKeyEncodeError>
179where
180    Keys: IntoEncodableValues,
181    Resolver: TypeResolver,
182    <Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
183{
184    let mut out = Vec::with_capacity(32);
185    encode_storage_key_with_info_to(
186        pallet_name,
187        storage_entry,
188        keys,
189        storage_info,
190        type_resolver,
191        &mut out,
192    )?;
193    Ok(out)
194}
195
196/// Encode a complete storage key for a given pallet and storage entry and a set of keys that
197/// are each able to be encoded via [`scale_encode::EncodeAsType`]. Write the response to the provided `Vec`.
198///
199/// Unlike [`encode_storage_key`], which obtains the storage info internally given the pallet and storage entry names,
200/// this function takes the storage info as an argument. This is useful if you already have the storage info available,
201/// for example if you are encoding multiple keys for the same storage entry.
202pub fn encode_storage_key_with_info_to<Resolver, Keys>(
203    pallet_name: &str,
204    storage_entry: &str,
205    keys: Keys,
206    storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
207    type_resolver: &Resolver,
208    out: &mut Vec<u8>,
209) -> Result<(), StorageKeyEncodeError>
210where
211    Keys: IntoEncodableValues,
212    Resolver: TypeResolver,
213    <Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
214{
215    let num_encodable_values = keys.num_encodable_values();
216
217    // If we provide more encodable values than there are keys, bail.
218    // If we provide less, that's ok and we just don't encode every part of the key
219    // (useful if eg iterating a bunch of entries under some prefix of a key).
220    if num_encodable_values > storage_info.keys.len() {
221        return Err(StorageKeyEncodeError::TooManyKeysProvided {
222            max_keys_expected: storage_info.keys.len(),
223        });
224    }
225
226    // Encode the prefix:
227    let prefix = encode_storage_key_prefix(pallet_name, storage_entry);
228    out.extend_from_slice(&prefix);
229
230    // Encode the keys:
231    let mut keys = keys.into_encodable_values();
232    let mut temp = Vec::with_capacity(32);
233    let iter = (0..num_encodable_values)
234        .zip(&*storage_info.keys)
235        .map(|(_, k)| k);
236
237    for key_info in iter {
238        keys.encode_next_value_to(key_info.key_id.clone(), type_resolver, &mut temp)
239            .map_err(StorageKeyEncodeError::EncodeError)?;
240
241        match key_info.hasher {
242            StorageHasher::Blake2_128 => {
243                let hash = sp_crypto_hashing::blake2_128(&temp);
244                out.extend_from_slice(&hash);
245            }
246            StorageHasher::Blake2_256 => {
247                let hash = sp_crypto_hashing::blake2_256(&temp);
248                out.extend_from_slice(&hash);
249            }
250            StorageHasher::Blake2_128Concat => {
251                let hash = sp_crypto_hashing::blake2_128(&temp);
252                out.extend_from_slice(&hash);
253                out.extend_from_slice(&temp);
254            }
255            StorageHasher::Twox128 => {
256                let hash = sp_crypto_hashing::twox_128(&temp);
257                out.extend_from_slice(&hash);
258            }
259            StorageHasher::Twox256 => {
260                let hash = sp_crypto_hashing::twox_256(&temp);
261                out.extend_from_slice(&hash);
262            }
263            StorageHasher::Twox64Concat => {
264                let hash = sp_crypto_hashing::twox_64(&temp);
265                out.extend_from_slice(&hash);
266                out.extend_from_slice(&temp);
267            }
268            StorageHasher::Identity => {
269                out.extend_from_slice(&temp);
270            }
271        }
272
273        // Clear our temp space ready for the next key.
274        temp.clear();
275    }
276
277    Ok(())
278}
279
280/// Encode the end part of a storage key (ie everything except for the prefix that can be encoded
281/// via [`encode_storage_key_prefix`]) for a given pallet and storage entry and a set of keys that are each
282/// able to be encoded via [`scale_encode::EncodeAsType`].
283///
284/// This is the same as [`encode_storage_key_suffix_to`], but returns the encoded key as a `Vec<u8>`, rather
285/// than accepting a mutable output buffer.
286///
287/// Prefer [`encode_storage_key`] if you need to encode the entire storage key at once and not just the suffix.
288///
289/// # Example
290///
291/// ```rust
292/// use frame_decode::storage::{ encode_storage_key_prefix, encode_storage_key_suffix };
293/// use frame_metadata::RuntimeMetadata;
294/// use parity_scale_codec::Decode;
295///
296/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
297/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
298///
299/// let account_id = [0u8; 32];
300///
301/// // System.Account needs only one key to point at a specific value; the account ID.
302/// // We just fake an account ID for this example  by providing 32 0 bytes, but anything
303/// // which would `scale_encode::EncodeAsType` into 32 bytes would work.
304/// let encoded_key_suffix = encode_storage_key_suffix(
305///     "System",
306///     "Account",
307///     &[account_id],
308///     &metadata,
309///     &metadata.types,
310/// ).unwrap();
311/// ```
312pub fn encode_storage_key_suffix<Info, Resolver, Keys>(
313    pallet_name: &str,
314    storage_entry: &str,
315    keys: Keys,
316    info: &Info,
317    type_resolver: &Resolver,
318) -> Result<Vec<u8>, StorageKeyEncodeError>
319where
320    Keys: IntoEncodableValues,
321    Info: StorageTypeInfo,
322    Info::TypeId: Clone + core::fmt::Debug,
323    Resolver: TypeResolver<TypeId = Info::TypeId>,
324{
325    // pre-allocate at least as many bytes as we need for the root/prefix.
326    let mut out = Vec::with_capacity(32);
327    encode_storage_key_suffix_to(
328        pallet_name,
329        storage_entry,
330        keys,
331        info,
332        type_resolver,
333        &mut out,
334    )?;
335    Ok(out)
336}
337
338/// Encode the end part of a storage key (ie everything except for the prefix that can be encoded
339/// via [`encode_storage_key_prefix`]) for a given pallet and storage entry and a set of keys that are each
340/// able to be encoded via [`scale_encode::EncodeAsType`].
341///
342/// This is the same as [`encode_storage_key_suffix`], but can be handed a `Vec` to encode the bytes to.
343///
344/// Prefer [`encode_storage_key_to`] if you need to encode the entire storage key at once and not just the suffix.
345///
346/// # Example
347///
348/// ```rust
349/// use frame_decode::storage::{ encode_storage_key_prefix, encode_storage_key_suffix_to };
350/// use frame_metadata::RuntimeMetadata;
351/// use parity_scale_codec::Decode;
352///
353/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
354/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
355///
356///
357/// let account_id = [0u8; 32];
358///
359/// let mut key = Vec::new();
360///
361/// // Write the suffix to the provided vec:
362/// let encoded_key_suffix = encode_storage_key_suffix_to(
363///     "System",
364///     "Account",
365///     &[account_id],
366///     &metadata,
367///     &metadata.types,
368///     &mut key
369/// ).unwrap();
370/// ```
371pub fn encode_storage_key_suffix_to<Info, Resolver, Keys>(
372    pallet_name: &str,
373    storage_entry: &str,
374    keys: Keys,
375    info: &Info,
376    type_resolver: &Resolver,
377    out: &mut Vec<u8>,
378) -> Result<(), StorageKeyEncodeError>
379where
380    Keys: IntoEncodableValues,
381    Info: StorageTypeInfo,
382    Info::TypeId: Clone + core::fmt::Debug,
383    Resolver: TypeResolver<TypeId = Info::TypeId>,
384{
385    let storage_info = info
386        .storage_info(pallet_name, storage_entry)
387        .map_err(|e| StorageKeyEncodeError::CannotGetInfo(e.into_owned()))?;
388
389    encode_storage_key_suffix_with_info_to(keys, &storage_info, type_resolver, out)
390}
391
392/// Encode the end part of a storage key (ie everything except for the prefix that can be encoded
393/// via [`encode_storage_key_prefix`]) for a given pallet and storage entry and a set of keys that are each
394/// able to be encoded via [`scale_encode::EncodeAsType`].
395///
396/// This is the same as [`encode_storage_key_suffix_to`], but is instead handed storage info that has been
397/// pre-computed via [`StorageTypeInfo::storage_info`]. This avoids having to retrieve this information multiple
398/// times if you'd like to encode many key suffixes for the same storage entry.
399///
400/// Prefer [`encode_storage_key_with_info_to`] if you need to encode the entire storage key at once and not
401/// just the suffix.
402pub fn encode_storage_key_suffix_with_info_to<Resolver, Keys>(
403    keys: Keys,
404    storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
405    type_resolver: &Resolver,
406    out: &mut Vec<u8>,
407) -> Result<(), StorageKeyEncodeError>
408where
409    Keys: IntoEncodableValues,
410    Resolver: TypeResolver,
411    <Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
412{
413    let num_encodable_values = keys.num_encodable_values();
414
415    // If we provide more encodable values than there are keys, bail.
416    // If we provide less, that's ok and we just don't encode every part of the key
417    // (useful if eg iterating a bunch of entries under some prefix of a key).
418    if num_encodable_values > storage_info.keys.len() {
419        return Err(StorageKeyEncodeError::TooManyKeysProvided {
420            max_keys_expected: storage_info.keys.len(),
421        });
422    }
423
424    // Encode the keys:
425    let mut keys = keys.into_encodable_values();
426    let mut temp = Vec::with_capacity(32);
427    let iter = (0..num_encodable_values)
428        .zip(&*storage_info.keys)
429        .map(|(_, k)| k);
430
431    for key_info in iter {
432        keys.encode_next_value_to(key_info.key_id.clone(), type_resolver, &mut temp)
433            .map_err(StorageKeyEncodeError::EncodeError)?;
434
435        match key_info.hasher {
436            StorageHasher::Blake2_128 => {
437                let hash = sp_crypto_hashing::blake2_128(&temp);
438                out.extend_from_slice(&hash);
439            }
440            StorageHasher::Blake2_256 => {
441                let hash = sp_crypto_hashing::blake2_256(&temp);
442                out.extend_from_slice(&hash);
443            }
444            StorageHasher::Blake2_128Concat => {
445                let hash = sp_crypto_hashing::blake2_128(&temp);
446                out.extend_from_slice(&hash);
447                out.extend_from_slice(&temp);
448            }
449            StorageHasher::Twox128 => {
450                let hash = sp_crypto_hashing::twox_128(&temp);
451                out.extend_from_slice(&hash);
452            }
453            StorageHasher::Twox256 => {
454                let hash = sp_crypto_hashing::twox_256(&temp);
455                out.extend_from_slice(&hash);
456            }
457            StorageHasher::Twox64Concat => {
458                let hash = sp_crypto_hashing::twox_64(&temp);
459                out.extend_from_slice(&hash);
460                out.extend_from_slice(&temp);
461            }
462            StorageHasher::Identity => {
463                out.extend_from_slice(&temp);
464            }
465        }
466
467        // Clear our temp space ready for the next key.
468        temp.clear();
469    }
470
471    Ok(())
472}
473
474#[cfg(test)]
475mod test {
476    use super::*;
477
478    #[test]
479    fn test_encode_storage_key() {
480        use crate::storage::encode_storage_key;
481        use frame_metadata::RuntimeMetadata;
482        use parity_scale_codec::Decode;
483
484        let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
485        let RuntimeMetadata::V14(metadata) =
486            RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap()
487        else {
488            return;
489        };
490
491        let account_id = [0u8; 32];
492
493        // System.Account needs only one key to point at a specific value; the account ID.
494        // We just fake an account ID for this example by providing 32 0 bytes, but anything
495        // which would `scale_encode::EncodeAsType` into 32 bytes should work.
496        encode_storage_key(
497            "System",
498            "Account",
499            &[account_id],
500            &metadata,
501            &metadata.types,
502        )
503        .expect("Encoding should work");
504        encode_storage_key(
505            "System",
506            "Account",
507            &(account_id,),
508            &metadata,
509            &metadata.types,
510        )
511        .expect("Encoding should work");
512
513        // We provide no additional keys, so we should get the prefix only.
514        let out = encode_storage_key("System", "Account", &(), &metadata, &metadata.types)
515            .expect("Encoding should work");
516        assert_eq!(&out, &encode_storage_key_prefix("System", "Account"));
517
518        // We provide too many additional keys so should get an error.
519        let err = encode_storage_key(
520            "System",
521            "Account",
522            &(account_id, 123u16),
523            &metadata,
524            &metadata.types,
525        );
526        assert!(matches!(
527            err,
528            Err(StorageKeyEncodeError::TooManyKeysProvided {
529                max_keys_expected: 1
530            })
531        ));
532    }
533}