subxt_core/storage/
address.rs

1// Copyright 2019-2024 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
5//! Construct addresses to access storage entries with.
6
7use crate::{
8    dynamic::DecodedValueThunk,
9    error::{Error, MetadataError},
10    metadata::{DecodeWithMetadata, Metadata},
11    utils::Yes,
12};
13use derive_where::derive_where;
14
15use alloc::borrow::{Cow, ToOwned};
16use alloc::string::String;
17use alloc::vec::Vec;
18
19// Re-export types used here:
20pub use super::storage_key::{StaticStorageKey, StorageHashers, StorageHashersIter, StorageKey};
21
22/// This represents a storage address. Anything implementing this trait
23/// can be used to fetch and iterate over storage entries.
24pub trait Address {
25    /// The target type of the value that lives at this address.
26    type Target: DecodeWithMetadata;
27    /// The keys type used to construct this address.
28    type Keys: StorageKey;
29    /// Can an entry be fetched from this address?
30    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
31    type IsFetchable;
32    /// Can a default entry be obtained from this address?
33    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
34    type IsDefaultable;
35    /// Can this address be iterated over?
36    /// Set this type to [`Yes`] to enable the corresponding calls to be made.
37    type IsIterable;
38
39    /// The name of the pallet that the entry lives under.
40    fn pallet_name(&self) -> &str;
41
42    /// The name of the entry in a given pallet that the item is at.
43    fn entry_name(&self) -> &str;
44
45    /// Output the non-prefix bytes; that is, any additional bytes that need
46    /// to be appended to the key to dig into maps.
47    fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error>;
48
49    /// An optional hash which, if present, will be checked against
50    /// the node metadata to confirm that the return type matches what
51    /// we are expecting.
52    fn validation_hash(&self) -> Option<[u8; 32]> {
53        None
54    }
55}
56
57/// A concrete storage address. This can be created from static values (ie those generated
58/// via the `subxt` macro) or dynamic values via [`dynamic`].
59#[derive_where(Clone, Debug, Eq, Ord, PartialEq, PartialOrd; Keys)]
60pub struct DefaultAddress<Keys: StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> {
61    pallet_name: Cow<'static, str>,
62    entry_name: Cow<'static, str>,
63    keys: Keys,
64    validation_hash: Option<[u8; 32]>,
65    _marker: core::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>,
66}
67
68/// A storage address constructed by the static codegen.
69pub type StaticAddress<Keys, ReturnTy, Fetchable, Defaultable, Iterable> =
70    DefaultAddress<Keys, ReturnTy, Fetchable, Defaultable, Iterable>;
71/// A typical storage address constructed at runtime rather than via the `subxt` macro; this
72/// has no restriction on what it can be used for (since we don't statically know).
73pub type DynamicAddress<Keys> = DefaultAddress<Keys, DecodedValueThunk, Yes, Yes, Yes>;
74
75impl<Keys: StorageKey> DynamicAddress<Keys> {
76    /// Creates a new dynamic address. As `Keys` you can use a `Vec<scale_value::Value>`
77    pub fn new(pallet_name: impl Into<String>, entry_name: impl Into<String>, keys: Keys) -> Self {
78        Self {
79            pallet_name: Cow::Owned(pallet_name.into()),
80            entry_name: Cow::Owned(entry_name.into()),
81            keys,
82            validation_hash: None,
83            _marker: core::marker::PhantomData,
84        }
85    }
86}
87
88impl<Keys, ReturnTy, Fetchable, Defaultable, Iterable>
89    DefaultAddress<Keys, ReturnTy, Fetchable, Defaultable, Iterable>
90where
91    Keys: StorageKey,
92    ReturnTy: DecodeWithMetadata,
93{
94    /// Create a new [`Address`] using static strings for the pallet and call name.
95    /// This is only expected to be used from codegen.
96    #[doc(hidden)]
97    pub fn new_static(
98        pallet_name: &'static str,
99        entry_name: &'static str,
100        keys: Keys,
101        hash: [u8; 32],
102    ) -> Self {
103        Self {
104            pallet_name: Cow::Borrowed(pallet_name),
105            entry_name: Cow::Borrowed(entry_name),
106            keys,
107            validation_hash: Some(hash),
108            _marker: core::marker::PhantomData,
109        }
110    }
111}
112
113impl<Keys, ReturnTy, Fetchable, Defaultable, Iterable>
114    DefaultAddress<Keys, ReturnTy, Fetchable, Defaultable, Iterable>
115where
116    Keys: StorageKey,
117    ReturnTy: DecodeWithMetadata,
118{
119    /// Do not validate this storage entry prior to accessing it.
120    pub fn unvalidated(self) -> Self {
121        Self {
122            validation_hash: None,
123            ..self
124        }
125    }
126
127    /// Return bytes representing the root of this storage entry (a hash of the pallet and entry name).
128    pub fn to_root_bytes(&self) -> Vec<u8> {
129        super::get_address_root_bytes(self)
130    }
131}
132
133impl<Keys, ReturnTy, Fetchable, Defaultable, Iterable> Address
134    for DefaultAddress<Keys, ReturnTy, Fetchable, Defaultable, Iterable>
135where
136    Keys: StorageKey,
137    ReturnTy: DecodeWithMetadata,
138{
139    type Target = ReturnTy;
140    type Keys = Keys;
141    type IsFetchable = Fetchable;
142    type IsDefaultable = Defaultable;
143    type IsIterable = Iterable;
144
145    fn pallet_name(&self) -> &str {
146        &self.pallet_name
147    }
148
149    fn entry_name(&self) -> &str {
150        &self.entry_name
151    }
152
153    fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error> {
154        let pallet = metadata.pallet_by_name_err(self.pallet_name())?;
155        let storage = pallet
156            .storage()
157            .ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?;
158        let entry = storage
159            .entry_by_name(self.entry_name())
160            .ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?;
161
162        let hashers = StorageHashers::new(entry.entry_type(), metadata.types())?;
163        self.keys
164            .encode_storage_key(bytes, &mut hashers.iter(), metadata.types())?;
165        Ok(())
166    }
167
168    fn validation_hash(&self) -> Option<[u8; 32]> {
169        self.validation_hash
170    }
171}
172
173/// Construct a new dynamic storage lookup.
174pub fn dynamic<Keys: StorageKey>(
175    pallet_name: impl Into<String>,
176    entry_name: impl Into<String>,
177    storage_entry_keys: Keys,
178) -> DynamicAddress<Keys> {
179    DynamicAddress::new(pallet_name, entry_name, storage_entry_keys)
180}