gear_subxt/storage/
storage_address.rs

1// Copyright 2019-2023 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use crate::{
6    dynamic::{DecodedValueThunk, Value},
7    error::{Error, MetadataError, StorageAddressError},
8    metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata},
9    utils::{Encoded, Static},
10};
11use scale_info::TypeDef;
12use std::borrow::Cow;
13use subxt_metadata::{StorageEntryType, StorageHasher};
14
15/// This represents a storage address. Anything implementing this trait
16/// can be used to fetch and iterate over storage entries.
17pub trait StorageAddress {
18    /// The target type of the value that lives at this address.
19    type Target: DecodeWithMetadata;
20    /// Can an entry be fetched from this address?
21    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
22    type IsFetchable;
23    /// Can a default entry be obtained from this address?
24    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
25    type IsDefaultable;
26    /// Can this address be iterated over?
27    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
28    type IsIterable;
29
30    /// The name of the pallet that the entry lives under.
31    fn pallet_name(&self) -> &str;
32
33    /// The name of the entry in a given pallet that the item is at.
34    fn entry_name(&self) -> &str;
35
36    /// Output the non-prefix bytes; that is, any additional bytes that need
37    /// to be appended to the key to dig into maps.
38    fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error>;
39
40    /// An optional hash which, if present, will be checked against
41    /// the node metadata to confirm that the return type matches what
42    /// we are expecting.
43    fn validation_hash(&self) -> Option<[u8; 32]> {
44        None
45    }
46}
47
48/// Used to signal whether a [`StorageAddress`] can be iterated,
49/// fetched and returned with a default value in the type system.
50pub struct Yes;
51
52/// A concrete storage address. This can be created from static values (ie those generated
53/// via the `subxt` macro) or dynamic values via [`dynamic`] and [`dynamic_root`].
54pub struct Address<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> {
55    pallet_name: Cow<'static, str>,
56    entry_name: Cow<'static, str>,
57    storage_entry_keys: Vec<StorageKey>,
58    validation_hash: Option<[u8; 32]>,
59    _marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>,
60}
61
62/// A typical storage address constructed at runtime rather than via the `subxt` macro; this
63/// has no restriction on what it can be used for (since we don't statically know).
64pub type DynamicAddress<StorageKey> = Address<StorageKey, DecodedValueThunk, Yes, Yes, Yes>;
65
66impl<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable>
67    Address<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable>
68where
69    StorageKey: EncodeWithMetadata,
70    ReturnTy: DecodeWithMetadata,
71{
72    /// Create a new [`Address`] to use to access a storage entry.
73    pub fn new(
74        pallet_name: impl Into<String>,
75        entry_name: impl Into<String>,
76        storage_entry_keys: Vec<StorageKey>,
77    ) -> Self {
78        Self {
79            pallet_name: Cow::Owned(pallet_name.into()),
80            entry_name: Cow::Owned(entry_name.into()),
81            storage_entry_keys: storage_entry_keys.into_iter().collect(),
82            validation_hash: None,
83            _marker: std::marker::PhantomData,
84        }
85    }
86
87    /// Create a new [`Address`] using static strings for the pallet and call name.
88    /// This is only expected to be used from codegen.
89    #[doc(hidden)]
90    pub fn new_static(
91        pallet_name: &'static str,
92        entry_name: &'static str,
93        storage_entry_keys: Vec<StorageKey>,
94        hash: [u8; 32],
95    ) -> Self {
96        Self {
97            pallet_name: Cow::Borrowed(pallet_name),
98            entry_name: Cow::Borrowed(entry_name),
99            storage_entry_keys: storage_entry_keys.into_iter().collect(),
100            validation_hash: Some(hash),
101            _marker: std::marker::PhantomData,
102        }
103    }
104
105    /// Do not validate this storage entry prior to accessing it.
106    pub fn unvalidated(self) -> Self {
107        Self {
108            validation_hash: None,
109            ..self
110        }
111    }
112
113    /// Return bytes representing the root of this storage entry (ie a hash of
114    /// the pallet and entry name). Use [`crate::storage::StorageClient::address_bytes()`]
115    /// to obtain the bytes representing the entire address.
116    pub fn to_root_bytes(&self) -> Vec<u8> {
117        super::utils::storage_address_root_bytes(self)
118    }
119}
120
121impl<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> StorageAddress
122    for Address<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable>
123where
124    StorageKey: EncodeWithMetadata,
125    ReturnTy: DecodeWithMetadata,
126{
127    type Target = ReturnTy;
128    type IsFetchable = Fetchable;
129    type IsDefaultable = Defaultable;
130    type IsIterable = Iterable;
131
132    fn pallet_name(&self) -> &str {
133        &self.pallet_name
134    }
135
136    fn entry_name(&self) -> &str {
137        &self.entry_name
138    }
139
140    fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error> {
141        let pallet = metadata.pallet_by_name_err(self.pallet_name())?;
142        let storage = pallet
143            .storage()
144            .ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?;
145        let entry = storage
146            .entry_by_name(self.entry_name())
147            .ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?;
148
149        match entry.entry_type() {
150            StorageEntryType::Plain(_) => {
151                if !self.storage_entry_keys.is_empty() {
152                    Err(StorageAddressError::WrongNumberOfKeys {
153                        expected: 0,
154                        actual: self.storage_entry_keys.len(),
155                    }
156                    .into())
157                } else {
158                    Ok(())
159                }
160            }
161            StorageEntryType::Map {
162                hashers, key_ty, ..
163            } => {
164                let ty = metadata
165                    .types()
166                    .resolve(*key_ty)
167                    .ok_or(MetadataError::TypeNotFound(*key_ty))?;
168
169                // If the key is a tuple, we encode each value to the corresponding tuple type.
170                // If the key is not a tuple, encode a single value to the key type.
171                let type_ids = match &ty.type_def {
172                    TypeDef::Tuple(tuple) => {
173                        either::Either::Left(tuple.fields.iter().map(|f| f.id))
174                    }
175                    _other => either::Either::Right(std::iter::once(*key_ty)),
176                };
177
178                if type_ids.len() != self.storage_entry_keys.len() {
179                    return Err(StorageAddressError::WrongNumberOfKeys {
180                        expected: type_ids.len(),
181                        actual: self.storage_entry_keys.len(),
182                    }
183                    .into());
184                }
185
186                if hashers.len() == 1 {
187                    // One hasher; hash a tuple of all SCALE encoded bytes with the one hash function.
188                    let mut input = Vec::new();
189                    let iter = self.storage_entry_keys.iter().zip(type_ids);
190                    for (key, type_id) in iter {
191                        key.encode_with_metadata(type_id, metadata, &mut input)?;
192                    }
193                    hash_bytes(&input, &hashers[0], bytes);
194                    Ok(())
195                } else if hashers.len() == type_ids.len() {
196                    let iter = self.storage_entry_keys.iter().zip(type_ids).zip(hashers);
197                    // A hasher per field; encode and hash each field independently.
198                    for ((key, type_id), hasher) in iter {
199                        let mut input = Vec::new();
200                        key.encode_with_metadata(type_id, metadata, &mut input)?;
201                        hash_bytes(&input, hasher, bytes);
202                    }
203                    Ok(())
204                } else {
205                    // Mismatch; wrong number of hashers/fields.
206                    Err(StorageAddressError::WrongNumberOfHashers {
207                        hashers: hashers.len(),
208                        fields: type_ids.len(),
209                    }
210                    .into())
211                }
212            }
213        }
214    }
215
216    fn validation_hash(&self) -> Option<[u8; 32]> {
217        self.validation_hash
218    }
219}
220
221/// A static storage key; this is some pre-encoded bytes
222/// likely provided by the generated interface.
223pub type StaticStorageMapKey = Static<Encoded>;
224
225// Used in codegen to construct the above.
226#[doc(hidden)]
227pub fn make_static_storage_map_key<T: codec::Encode>(t: T) -> StaticStorageMapKey {
228    Static(Encoded(t.encode()))
229}
230
231/// Construct a new dynamic storage lookup to the root of some entry.
232pub fn dynamic_root(
233    pallet_name: impl Into<String>,
234    entry_name: impl Into<String>,
235) -> DynamicAddress<Value> {
236    DynamicAddress::new(pallet_name, entry_name, vec![])
237}
238
239/// Construct a new dynamic storage lookup.
240pub fn dynamic<StorageKey: EncodeWithMetadata>(
241    pallet_name: impl Into<String>,
242    entry_name: impl Into<String>,
243    storage_entry_keys: Vec<StorageKey>,
244) -> DynamicAddress<StorageKey> {
245    DynamicAddress::new(pallet_name, entry_name, storage_entry_keys)
246}
247
248/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly.
249fn hash_bytes(input: &[u8], hasher: &StorageHasher, bytes: &mut Vec<u8>) {
250    match hasher {
251        StorageHasher::Identity => bytes.extend(input),
252        StorageHasher::Blake2_128 => bytes.extend(sp_core_hashing::blake2_128(input)),
253        StorageHasher::Blake2_128Concat => {
254            bytes.extend(sp_core_hashing::blake2_128(input));
255            bytes.extend(input);
256        }
257        StorageHasher::Blake2_256 => bytes.extend(sp_core_hashing::blake2_256(input)),
258        StorageHasher::Twox128 => bytes.extend(sp_core_hashing::twox_128(input)),
259        StorageHasher::Twox256 => bytes.extend(sp_core_hashing::twox_256(input)),
260        StorageHasher::Twox64Concat => {
261            bytes.extend(sp_core_hashing::twox_64(input));
262            bytes.extend(input);
263        }
264    }
265}