use crate::{
backend::{BackendExt, BlockRef},
client::{OfflineClientT, OnlineClientT},
config::{Config, HashFor},
error::StorageError,
};
use derive_where::derive_where;
use futures::StreamExt;
use pezkuwi_subxt_core::{
storage::{address::Address, PrefixOf},
utils::{Maybe, Yes},
Metadata,
};
use std::marker::PhantomData;
pub use pezkuwi_subxt_core::storage::{StorageKeyValue, StorageValue};
#[derive_where(Clone; Client)]
pub struct StorageClientAt<T: Config, Client> {
client: Client,
metadata: Metadata,
block_ref: BlockRef<HashFor<T>>,
_marker: PhantomData<T>,
}
impl<T, Client> StorageClientAt<T, Client>
where
T: Config,
Client: OfflineClientT<T>,
{
pub(crate) fn new(client: Client, block_ref: BlockRef<HashFor<T>>) -> Self {
let metadata = client.metadata();
Self { client, metadata, block_ref, _marker: PhantomData }
}
}
impl<T, Client> StorageClientAt<T, Client>
where
T: Config,
Client: OfflineClientT<T>,
{
pub fn entry<Addr: Address>(
&self,
address: Addr,
) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsPlain>, StorageError> {
let inner = pezkuwi_subxt_core::storage::entry(address, &self.metadata)?;
Ok(StorageEntryClient {
inner,
client: self.client.clone(),
block_ref: self.block_ref.clone(),
_marker: core::marker::PhantomData,
})
}
}
impl<T, Client> StorageClientAt<T, Client>
where
T: Config,
Client: OnlineClientT<T>,
{
pub async fn fetch<Addr: Address>(
&self,
addr: Addr,
key_parts: Addr::KeyParts,
) -> Result<StorageValue<'_, Addr::Value>, StorageError> {
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
fetch(&entry, &self.client, self.block_ref.hash(), key_parts).await
}
pub async fn try_fetch<Addr: Address>(
&self,
addr: Addr,
key_parts: Addr::KeyParts,
) -> Result<Option<StorageValue<'_, Addr::Value>>, StorageError> {
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
try_fetch(&entry, &self.client, self.block_ref.hash(), key_parts).await
}
pub async fn iter<Addr: Address, KeyParts: PrefixOf<Addr::KeyParts>>(
&'_ self,
addr: Addr,
key_parts: KeyParts,
) -> Result<
impl futures::Stream<Item = Result<StorageKeyValue<'_, Addr>, StorageError>>
+ use<'_, Addr, Client, T, KeyParts>,
StorageError,
> {
let entry = pezkuwi_subxt_core::storage::entry(addr, &self.metadata)?;
iter(entry, &self.client, self.block_ref.hash(), key_parts).await
}
pub async fn fetch_raw(&self, key_bytes: Vec<u8>) -> Result<Vec<u8>, StorageError> {
let block_hash = self.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 mut key_bytes: Vec<u8> = vec![];
key_bytes.extend(&pezsp_crypto_hashing::twox_128(pallet_name.as_ref().as_bytes()));
key_bytes.extend(&pezsp_crypto_hashing::twox_128(b":__STORAGE_VERSION__:"));
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 StorageEntryClient<'atblock, T: Config, Client, Addr, IsPlain> {
inner: pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
client: Client,
block_ref: BlockRef<HashFor<T>>,
_marker: PhantomData<(T, IsPlain)>,
}
impl<'atblock, T, Client, Addr, IsPlain> StorageEntryClient<'atblock, T, Client, Addr, IsPlain>
where
T: Config,
Addr: Address,
{
pub fn pallet_name(&self) -> &str {
self.inner.pallet_name()
}
pub fn entry_name(&self) -> &str {
self.inner.entry_name()
}
pub fn is_plain(&self) -> bool {
self.inner.is_plain()
}
pub fn is_map(&self) -> bool {
self.inner.is_map()
}
pub fn default_value(&self) -> Option<StorageValue<'atblock, Addr::Value>> {
self.inner.default_value()
}
}
impl<'atblock, T, Client, Addr> StorageEntryClient<'atblock, T, Client, Addr, Yes>
where
T: Config,
Addr: Address,
Client: OnlineClientT<T>,
{
pub async fn fetch(&self) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
let value = self
.try_fetch()
.await?
.map_or_else(|| self.inner.default_value().ok_or(StorageError::NoValueFound), Ok)?;
Ok(value)
}
pub async fn try_fetch(
&self,
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
let value = self
.client
.backend()
.storage_fetch_value(self.key_prefix().to_vec(), self.block_ref.hash())
.await
.map_err(StorageError::CannotFetchValue)?
.map(|bytes| self.inner.value(bytes));
Ok(value)
}
pub fn key(&self) -> [u8; 32] {
self.inner.key_prefix()
}
pub fn key_prefix(&self) -> [u8; 32] {
self.inner.key_prefix()
}
}
impl<'atblock, T, Client, Addr> StorageEntryClient<'atblock, T, Client, Addr, Maybe>
where
T: Config,
Addr: Address,
Client: OnlineClientT<T>,
{
pub async fn fetch(
&self,
key_parts: Addr::KeyParts,
) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
fetch(&self.inner, &self.client, self.block_ref.hash(), key_parts).await
}
pub async fn try_fetch(
&self,
key_parts: Addr::KeyParts,
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
try_fetch(&self.inner, &self.client, self.block_ref.hash(), key_parts).await
}
pub async fn iter<KeyParts: PrefixOf<Addr::KeyParts>>(
&self,
key_parts: KeyParts,
) -> Result<
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
+ use<'atblock, Addr, Client, T, KeyParts>,
StorageError,
> {
iter(self.inner.clone(), &self.client, self.block_ref.hash(), key_parts).await
}
pub fn key(&self, key_parts: Addr::KeyParts) -> Result<Vec<u8>, StorageError> {
let key = self.inner.fetch_key(key_parts)?;
Ok(key)
}
pub fn iter_key<KeyParts: PrefixOf<Addr::KeyParts>>(
&self,
key_parts: KeyParts,
) -> Result<Vec<u8>, StorageError> {
let key = self.inner.iter_key(key_parts)?;
Ok(key)
}
pub fn key_prefix(&self) -> [u8; 32] {
self.inner.key_prefix()
}
}
async fn fetch<'atblock, T: Config, Client: OnlineClientT<T>, Addr: Address>(
entry: &pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
client: &Client,
block_hash: HashFor<T>,
key_parts: Addr::KeyParts,
) -> Result<StorageValue<'atblock, Addr::Value>, StorageError> {
let value = try_fetch(entry, client, block_hash, key_parts)
.await?
.or_else(|| entry.default_value())
.unwrap();
Ok(value)
}
async fn try_fetch<'atblock, T: Config, Client: OnlineClientT<T>, Addr: Address>(
entry: &pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
client: &Client,
block_hash: HashFor<T>,
key_parts: Addr::KeyParts,
) -> Result<Option<StorageValue<'atblock, Addr::Value>>, StorageError> {
let key = entry.fetch_key(key_parts)?;
let value = client
.backend()
.storage_fetch_value(key, block_hash)
.await
.map_err(StorageError::CannotFetchValue)?
.map(|bytes| entry.value(bytes))
.or_else(|| entry.default_value());
Ok(value)
}
async fn iter<
'atblock,
T: Config,
Client: OnlineClientT<T>,
Addr: Address,
KeyParts: PrefixOf<Addr::KeyParts>,
>(
entry: pezkuwi_subxt_core::storage::StorageEntry<'atblock, Addr>,
client: &Client,
block_hash: HashFor<T>,
key_parts: KeyParts,
) -> Result<
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
+ use<'atblock, Addr, Client, T, KeyParts>,
StorageError,
> {
let key_bytes = entry.iter_key(key_parts)?;
let stream = client
.backend()
.storage_fetch_descendant_values(key_bytes, block_hash)
.await
.map_err(StorageError::CannotIterateValues)?
.map(move |kv| {
let kv = match kv {
Ok(kv) => kv,
Err(e) => return Err(StorageError::StreamFailure(e)),
};
Ok(entry.key_value(kv.key, kv.value))
});
Ok(Box::pin(stream))
}