subxt_core/storage/mod.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//! Encode storage keys, decode storage values, and validate static storage addresses.
6//!
7//! # Example
8//!
9//! ```rust
10//! use subxt_signer::sr25519::dev;
11//! use subxt_macro::subxt;
12//! use subxt_core::storage;
13//! use subxt_core::metadata;
14//!
15//! // If we generate types without `subxt`, we need to point to `::subxt_core`:
16//! #[subxt(
17//! crate = "::subxt_core",
18//! runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale",
19//! )]
20//! pub mod polkadot {}
21//!
22//! // Some metadata we'll use to work with storage entries:
23//! let metadata_bytes = include_bytes!("../../../artifacts/polkadot_metadata_small.scale");
24//! let metadata = metadata::decode_from(&metadata_bytes[..]).unwrap();
25//!
26//! // Build a storage query to access account information.
27//! let account = dev::alice().public_key().into();
28//! let address = polkadot::storage().system().account(&account);
29//!
30//! // We can validate that the address is compatible with the given metadata.
31//! storage::validate(&address, &metadata).unwrap();
32//!
33//! // Encode the address to bytes. These can be sent to a node to query the value.
34//! storage::get_address_bytes(&address, &metadata).unwrap();
35//!
36//! // If we were to obtain a value back from the node at that address, we could
37//! // then decode it using the same address and metadata like so:
38//! let value_bytes = hex::decode("00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080").unwrap();
39//! let value = storage::decode_value(&mut &*value_bytes, &address, &metadata).unwrap();
40//!
41//! println!("Alice's account info: {value:?}");
42//! ```
43
44mod storage_key;
45mod utils;
46
47pub mod address;
48
49use crate::{error::MetadataError, metadata::DecodeWithMetadata, Error, Metadata};
50use address::Address;
51use alloc::vec::Vec;
52
53// This isn't a part of the public API, but expose here because it's useful in Subxt.
54#[doc(hidden)]
55pub use utils::lookup_storage_entry_details;
56
57/// When the provided `address` is statically generated via the `#[subxt]` macro, this validates
58/// that the shape of the storage value is the same as the shape expected by the static address.
59///
60/// When the provided `address` is dynamic (and thus does not come with any expectation of the
61/// shape of the constant value), this just returns `Ok(())`
62pub fn validate<Addr: Address>(address: &Addr, metadata: &Metadata) -> Result<(), Error> {
63 let Some(hash) = address.validation_hash() else {
64 return Ok(());
65 };
66
67 let pallet_name = address.pallet_name();
68 let entry_name = address.entry_name();
69
70 let pallet_metadata = metadata.pallet_by_name_err(pallet_name)?;
71
72 let Some(expected_hash) = pallet_metadata.storage_hash(entry_name) else {
73 return Err(MetadataError::IncompatibleCodegen.into());
74 };
75 if expected_hash != hash {
76 return Err(MetadataError::IncompatibleCodegen.into());
77 }
78 Ok(())
79}
80
81/// Given a storage address and some metadata, this encodes the address into bytes which can be
82/// handed to a node to retrieve the corresponding value.
83pub fn get_address_bytes<Addr: Address>(
84 address: &Addr,
85 metadata: &Metadata,
86) -> Result<Vec<u8>, Error> {
87 let mut bytes = Vec::new();
88 utils::write_storage_address_root_bytes(address, &mut bytes);
89 address.append_entry_bytes(metadata, &mut bytes)?;
90 Ok(bytes)
91}
92
93/// Given a storage address and some metadata, this encodes the root of the address (ie the pallet
94/// and storage entry part) into bytes. If the entry being addressed is inside a map, this returns
95/// the bytes needed to iterate over all of the entries within it.
96pub fn get_address_root_bytes<Addr: Address>(address: &Addr) -> Vec<u8> {
97 let mut bytes = Vec::new();
98 utils::write_storage_address_root_bytes(address, &mut bytes);
99 bytes
100}
101
102/// Given some storage value that we've retrieved from a node, the address used to retrieve it, and
103/// metadata from the node, this function attempts to decode the bytes into the target value specified
104/// by the address.
105pub fn decode_value<Addr: Address>(
106 bytes: &mut &[u8],
107 address: &Addr,
108 metadata: &Metadata,
109) -> Result<Addr::Target, Error> {
110 let pallet_name = address.pallet_name();
111 let entry_name = address.entry_name();
112
113 let (_, entry_metadata) =
114 utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?;
115 let value_ty_id = match entry_metadata.entry_type() {
116 subxt_metadata::StorageEntryType::Plain(ty) => *ty,
117 subxt_metadata::StorageEntryType::Map { value_ty, .. } => *value_ty,
118 };
119
120 let val = Addr::Target::decode_with_metadata(bytes, value_ty_id, metadata)?;
121 Ok(val)
122}
123
124/// Return the default value at a given storage address if one is available, or an error otherwise.
125pub fn default_value<Addr: Address>(
126 address: &Addr,
127 metadata: &Metadata,
128) -> Result<Addr::Target, Error> {
129 let pallet_name = address.pallet_name();
130 let entry_name = address.entry_name();
131
132 let (_, entry_metadata) =
133 utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?;
134 let value_ty_id = match entry_metadata.entry_type() {
135 subxt_metadata::StorageEntryType::Plain(ty) => *ty,
136 subxt_metadata::StorageEntryType::Map { value_ty, .. } => *value_ty,
137 };
138
139 let default_bytes = entry_metadata.default_bytes();
140 let val = Addr::Target::decode_with_metadata(&mut &*default_bytes, value_ty_id, metadata)?;
141 Ok(val)
142}