mod address;
mod prefix_of;
mod storage_entry;
mod storage_key;
mod storage_key_value;
mod storage_value;
use crate::backend::BackendExt;
use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT};
use crate::config::Config;
use crate::error::StorageError;
use core::marker::PhantomData;
use frame_decode::helpers::Entry;
use frame_decode::storage::StorageEntryInfo;
use std::borrow::Cow;
pub use address::{Address, DynamicAddress, StaticAddress, dynamic};
pub use prefix_of::PrefixOf;
pub use storage_entry::{StorageEntries, StorageEntry};
pub use storage_key::{StorageKey, StorageKeyPart};
pub use storage_key_value::StorageKeyValue;
pub use storage_value::StorageValue;
#[derive(Clone)]
pub struct StorageClient<'atblock, T, Client> {
client: &'atblock Client,
marker: PhantomData<T>,
}
impl<'atblock, T, Client> StorageClient<'atblock, T, Client> {
pub(crate) fn new(client: &'atblock Client) -> Self {
StorageClient {
client,
marker: PhantomData,
}
}
}
impl<'atblock, T: Config, Client: OfflineClientAtBlockT<T>> StorageClient<'atblock, T, Client> {
pub fn validate<Addr: Address>(&self, address: Addr) -> Result<(), StorageError> {
let Some(hash) = address.validation_hash() else {
return Ok(());
};
let pallet_name = address.pallet_name();
let entry_name = address.entry_name();
let pallet_metadata = self
.client
.metadata_ref()
.pallet_by_name(pallet_name)
.ok_or_else(|| StorageError::PalletNameNotFound(pallet_name.to_string()))?;
let storage_hash = pallet_metadata.storage_hash(entry_name).ok_or_else(|| {
StorageError::StorageEntryNotFound {
pallet_name: pallet_name.to_string(),
entry_name: entry_name.to_string(),
}
})?;
if storage_hash != hash {
Err(StorageError::IncompatibleCodegen)
} else {
Ok(())
}
}
pub fn entry<Addr: Address>(
&self,
address: Addr,
) -> Result<StorageEntry<'atblock, T, Client, Addr>, StorageError> {
self.validate(&address)?;
StorageEntry::new(self.client, address)
}
pub fn entries(&self) -> impl Iterator<Item = StorageEntryRef<'atblock, T, Client>> {
let metadata = self.client.metadata_ref();
Entry::tuples_of(metadata.storage_entries()).map(|(pallet_name, entry_name)| {
StorageEntryRef {
pallet_name: pallet_name.clone(),
entry_name,
client: self.client,
marker: std::marker::PhantomData,
}
})
}
}
impl<'atblock, T: Config, Client: OnlineClientAtBlockT<T>> StorageClient<'atblock, T, Client> {
pub async fn fetch<Addr: Address>(
&self,
addr: Addr,
key_parts: Addr::KeyParts,
) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
let entry = self.entry(addr)?;
entry.fetch(key_parts).await
}
pub async fn try_fetch<Addr: Address>(
&self,
addr: Addr,
key_parts: Addr::KeyParts,
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
let entry = self.entry(addr)?;
entry.try_fetch(key_parts).await
}
pub async fn iter<Addr: Address, KeyParts: PrefixOf<Addr::KeyParts>>(
&self,
addr: Addr,
key_parts: KeyParts,
) -> Result<StorageEntries<'atblock, Addr>, StorageError> {
let entry = self.entry(addr)?;
entry.iter(key_parts).await
}
pub async fn fetch_raw(&self, key_bytes: Vec<u8>) -> Result<Vec<u8>, StorageError> {
let block_hash = self.client.block_ref().hash();
let value = self
.client
.backend()
.storage_fetch_value(key_bytes, block_hash)
.await
.map_err(StorageError::CannotFetchValue)?
.ok_or(StorageError::NoValueFound)?;
Ok(value)
}
pub async fn storage_version(&self, pallet_name: impl AsRef<str>) -> Result<u16, StorageError> {
let key_bytes = frame_decode::storage::encode_storage_key_prefix(
pallet_name.as_ref(),
":__STORAGE_VERSION__:",
)
.to_vec();
let storage_version_bytes = self.fetch_raw(key_bytes).await?;
<u16 as codec::Decode>::decode(&mut &storage_version_bytes[..])
.map_err(StorageError::CannotDecodeStorageVersion)
}
pub async fn runtime_wasm_code(&self) -> Result<Vec<u8>, StorageError> {
self.fetch_raw(b":code".to_vec()).await
}
}
pub struct StorageEntryRef<'atblock, T, Client> {
pallet_name: Cow<'atblock, str>,
entry_name: Cow<'atblock, str>,
client: &'atblock Client,
marker: std::marker::PhantomData<T>,
}
impl<'atblock, Client, T> StorageEntryRef<'atblock, T, Client>
where
T: Config,
Client: OfflineClientAtBlockT<T>,
{
pub fn pallet_name(&self) -> &str {
&self.pallet_name
}
pub fn entry_name(&self) -> &str {
&self.entry_name
}
pub fn entry(
&self,
) -> Result<StorageEntry<'atblock, T, Client, address::DynamicAddress>, StorageError> {
let addr = address::dynamic(&*self.pallet_name, &*self.entry_name);
StorageEntry::new(self.client, addr)
}
}