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}